A minimal reactive framework built for learning purposes to understand how modern frontend frameworks like Vue.js and React work under the hood. Flexi demonstrates the core concepts of reactivity and virtual DOM diffing.
This project is designed to help you understand:
- Reactivity System: How automatic dependency tracking and re-rendering works
- Virtual DOM: How VDOM diffing and patching algorithms function
- Component Lifecycle: How frameworks manage component updates
- State Management: How reactive state triggers UI updates
# Clone the repository
git clone <your-repo-url>
cd Flexi
# Install dependencies
pnpm install
# Start development server
pnpm devimport { Flexi } from "./src/index";
import { h } from "./src/vdom";
const app = new Flexi({
el: "#app",
state: { count: 0 },
render: (state) =>
h("div", { class: "counter" }, [
h("h1", {}, [`Count: ${state.count}`]),
h("button", { onClick: () => state.count++ }, ["Increment"]),
h("button", { onClick: () => state.count-- }, ["Decrement"]),
]),
});The reactivity system is the heart of modern frameworks. It automatically tracks dependencies and re-runs effects when data changes.
- Reactive Objects: Wrapped with
Proxyto intercept property access - Dependency Tracking: Uses
MapandSetto track which effects depend on which properties - Effect System:
autorunfunction that automatically re-executes when dependencies change
// How it works internally:
const state = reactive({ count: 0 });
autorun(() => {
console.log(`Count is: ${state.count}`); // Automatically tracks state.count
});
state.count++; // Automatically triggers the effect above- Proxy API: How JavaScript proxies enable reactive behavior
- Dependency Collection: How frameworks know what to re-run
Virtual DOM is an abstraction over the real DOM that enables efficient updates.
- VNode Structure: Lightweight representation of DOM elements
- Hyperscript Function:
h()function for creating VNodes - Element Creation: Converting VNodes to real DOM elements
// VNode structure:
interface VNode {
tag: string;
props: Record<string, any>;
children: Array<VNode | string>;
}
// Creating VNodes:
const vnode = h("div", { class: "container" }, [
h("h1", {}, ["Hello World"]),
h("button", { onClick: handleClick }, ["Click me"])
]);- DOM Abstraction: Why VDOM exists and its benefits
- Element Creation: How VNodes become real DOM elements
- Props Handling: How attributes and event handlers are applied
The diffing algorithm compares old and new VNodes to determine the minimal DOM updates needed.
- Tree Diffing: Comparing VNode trees efficiently
- Keyed Updates: Optimizing list updates
- DOM Patching: Applying changes to the real DOM
// The patch function signature:
patch(parent: Node, oldNode: VNode | string | null, newNode: VNode | string)- Diffing Strategies: How to efficiently compare trees
- Update Batching: Minimizing DOM operations
- Error Handling: Dealing with DOM structure mismatches
src/
βββ index.ts # Main framework class
βββ reactivity.ts # Reactive state management
βββ vdom.ts # Virtual DOM implementation
βββ patch.ts # Diffing and patching algorithm
- Initialization: Create reactive state and render initial VDOM
- Dependency Tracking:
autoruntracks which state properties are accessed - State Changes: When state changes, effects are automatically triggered
- Re-rendering: New VDOM tree is created from current state
- Diffing: Old and new VDOM trees are compared
- Patching: Minimal DOM updates are applied
A simple counter demonstrating basic reactivity:
const app = new Flexi({
el: "#app",
state: { count: 0 },
render: (state) =>
h("div", { class: "counter" }, [
h("h1", {}, [`Count: ${state.count}`]),
h("button", { onClick: () => state.count++ }, ["Increment"]),
h("button", { onClick: () => state.count-- }, ["Decrement"]),
]),
});const app = new Flexi({
el: "#app",
state: {
todos: [],
newTodo: ""
},
render: (state) =>
h("div", { class: "todo-app" }, [
h("input", {
value: state.newTodo,
onInput: (e) => state.newTodo = e.target.value
}, []),
h("button", {
onClick: () => {
if (state.newTodo.trim()) {
state.todos.push(state.newTodo);
state.newTodo = "";
}
}
}, ["Add Todo"]),
h("ul", {},
state.todos.map(todo => h("li", {}, [todo]))
)
]),
});# Start development server
pnpm dev
# Build for production
pnpm buildFlexi/
βββ src/ # Core framework code
βββ examples/ # Example applications
βββ dist/ # Built files
βββ package.json # Dependencies and scripts
// When you do this:
state.count++;
// Here's what happens internally:
// 1. Proxy intercepts the set operation
// 2. Notifies all effects that depend on 'count'
// 3. Triggers re-render
// 4. Creates new VDOM tree
// 5. Diffs with old tree
// 6. Patches DOM with minimal changesautorun(() => {
// This effect depends on state.count
console.log(state.count);
// And this effect depends on state.name
console.log(state.name);
});
// When state.count changes, only the first console.log re-runs
// When state.name changes, only the second console.log re-runs// Old VDOM:
h("div", {}, [h("span", {}, ["Hello"])])
// New VDOM:
h("div", {}, [h("span", {}, ["World"])])
// Diffing result:
// - Same tag (div) β
// - Same tag (span) β
// - Different text content β Update textContentThis roadmap outlines planned features and enhancements for the Flexi framework. Each item represents a learning opportunity to understand how modern frameworks work.
- Basic reactive objects with Proxy
- Dependency tracking with Simple Map and Set
- Autorun effects
- Computed Properties: Derived state that automatically updates
- Watchers: React to specific state changes
- Reactive Arrays: Special handling for array mutations
- Deep Reactivity: Nested object reactivity
- Reactive Maps and Sets: Collection reactivity
- Basic VNode structure
- Hyperscript function (h)
- Fragment Support: Multiple root elements
- Portal Support: Render outside component tree
- Suspense: Async component loading
- Error Boundaries: Error handling components
- Basic tree diffing
- Keyed Lists: Optimized list rendering
- Component Diffing: Smart component updates
- Batch Updates: Reduce DOM operations
- Async Rendering: Non-blocking updates
- Template Parser: Parse HTML-like templates
- AST Generation: Abstract Syntax Tree for templates
- Code Generation: Convert templates to render functions
- Directive Support: f-if, f-for, f-model, etc.
- Conditional Rendering:
f-ifandf-else - List Rendering:
f-forwith keys - Two-way Binding:
f-modelfor forms - Event Handling:
@click,@input, etc. - Class and Style Binding: Dynamic styling
- Slot System: Content projection
- Component Definition: Reusable component structure
- Props System: Parent-child communication
- Emit System: Child-parent communication
- Component Lifecycle: mounted, updated, unmounted
- Component Registration: Global and local components
- Higher-Order Components: Component composition
- Render Props: Function as children pattern
- Context API: Cross-component state sharing
- Provider Pattern: Dependency injection
- Reactive state objects
- State Composition: Combining multiple state sources
- State Persistence: LocalStorage integration
- State Validation: Schema validation
- Store Pattern: Centralized state management
- Actions and Mutations: Predictable state changes
- State Subscriptions: React to global changes
- State DevTools: Debugging and time-travel
- Memoization: Prevent unnecessary re-renders
- Lazy Loading: Code splitting and dynamic imports
- Virtual Scrolling: Large list optimization
- Tree Shaking: Remove unused code
- Garbage Collection: Proper cleanup of effects
- Memory Leak Prevention: Component cleanup
- Weak References: Prevent memory leaks
- Hot Module Replacement: Fast development cycles
- Source Maps: Debug original code
- Error Overlay: Better error reporting
- Performance Profiling: Identify bottlenecks
- Reactivity Inspector: Visualize dependency graph
- VDOM Inspector: Examine virtual DOM structure
- State Inspector: Monitor state changes
- Timeline: Track render cycles
- Module Bundler: Rollup/Vite integration
- Tree Shaking: Remove dead code
- Code Splitting: Dynamic imports
- Asset Optimization: CSS, images, fonts
- TypeScript Support: Full type safety
- JSX Support: Alternative to hyperscript
- CSS-in-JS: Scoped styles
- Static Analysis: Linting and formatting
- Component Testing: Test individual components
- Reactivity Testing: Test reactive behavior
- VDOM Testing: Test virtual DOM operations
- Mock System: Mock dependencies
- End-to-End Testing: Full application testing
- Visual Regression: UI consistency testing
- Performance Testing: Benchmark rendering
- Interactive Tutorials: Step-by-step guides
- Code Sandbox: Online playground
- Video Tutorials: Visual learning
- Best Practices: Framework conventions
- Todo App: Basic CRUD operations
- Shopping Cart: State management
- Real-time Chat: WebSocket integration
- Dashboard: Complex UI patterns
- Game: Interactive applications
- Plugin System: Extensible architecture
- Router: Client-side routing
- HTTP Client: API integration
- Form Validation: Input validation
- Animation: Transition system
- Package Registry: Share components
- Template Gallery: Pre-built templates
- Community Examples: User contributions
- Documentation Site: Comprehensive docs
Current Status: π‘ In Progress (Core features implemented)
Next Milestone: Template Engine and Component System
Estimated Completion: Q3 2025
To better understand the concepts implemented here:
- Reactivity: Read about Vue 3's Composition API and React's useState
- Virtual DOM: Study React's reconciliation algorithm
- Diffing: Learn about the diffing strategies used in React and Vue
- Proxy API: MDN documentation on JavaScript Proxies
This is a learning project! Feel free to:
- Add more examples
- Implement additional features (computed properties, watchers, etc.)
- Improve the diffing algorithm
- Add tests
- Document more learning concepts
MIT License - feel free to use this for learning and experimentation!
Happy Learning! π
This framework demonstrates the fundamental concepts that power modern frontend frameworks. Understanding these concepts will make you a better developer and help you appreciate the complexity and elegance of frameworks like React, Vue, and Svelte.