The Mini-Framework is a lightweight JavaScript framework that enables building reactive web applications with a component-based architecture. It follows the principle of "inversion of control" - instead of you calling the framework's methods, the framework calls your code at appropriate times.
The framework provides several key features:
- Virtual DOM for efficient DOM manipulation
- Component-based architecture
- State management with hooks
- Side effects management
- Event handling
- Routing system
The framework uses a virtual DOM approach to efficiently update the actual DOM. Instead of directly manipulating the DOM (which is slow), it creates a lightweight JavaScript representation of the DOM, compares changes, and only updates what's necessary.
Components are functions that return virtual DOM structures. They can be reused, nested, and composed to build complex UIs.
The framework provides React-like hooks for managing state and side effects:
useState: For reactive state managementuseEffect: For handling side effects- Lifecycle hooks for mounting and unmounting components
The built-in router allows you to manage different views in your application and synchronize the URL with application state.
The framework consists of several core modules:
vdom.js: Handles virtual DOM creation and renderingdiffing.js: Implements the diffing algorithm to update the DOM efficientlyhooks.js: Provides state and effect managementrouter.js: Handles routing between different views
In the Mini-Framework, DOM elements are represented as JavaScript objects with a specific structure:
const element = {
tag: 'div', // HTML tag name
attrs: { // HTML attributes and event handlers
class: 'container',
id: 'main',
style: { color: 'red' },
onClick: () => alert('Clicked!')
},
children: [ // Child elements (can be strings or other elements)
'Text content', // Text nodes are just strings
{ // Nested elements
tag: 'span',
attrs: { class: 'highlight' },
children: ['Highlighted text']
}
]
};// Creating a button element
const button = {
tag: 'button',
attrs: {
class: 'btn primary',
id: 'submit-btn'
},
children: ['Submit']
};Components are functions that return virtual DOM elements:
function Greeting({ name }) {
return {
tag: 'h1',
attrs: { class: 'greeting' },
children: [`Hello, ${name}!`]
};
}
// Using the component
const greeting = {
component: Greeting,
props: { name: 'World' }
};Event handlers are added through the attrs object using the on prefix followed by the event name:
function Button({ text, onClick }) {
return {
tag: 'button',
attrs: {
class: 'btn',
onClick: onClick, // Click event
onMouseEnter: () => console.log('Mouse entered'),
onMouseLeave: () => console.log('Mouse left')
},
children: [text]
};
}Event handlers automatically receive the native DOM event object:
function Form() {
const [text, setText] = useState('');
function handleSubmit(event) {
event.preventDefault(); // Prevent form submission
console.log('Submitted:', text);
setText('');
}
return {
tag: 'form',
attrs: {
onSubmit: handleSubmit
},
children: [
{
tag: 'input',
attrs: {
type: 'text',
value: text,
onInput: (e) => setText(e.target.value)
}
},
{
tag: 'button',
attrs: { type: 'submit' },
children: ['Submit']
}
]
};
}Elements can be nested by including them in the children array:
function Card({ title, content }) {
return {
tag: 'div',
attrs: { class: 'card' },
children: [
{
tag: 'h2',
attrs: { class: 'card-title' },
children: [title]
},
{
tag: 'div',
attrs: { class: 'card-content' },
children: [content]
}
]
};
}You can also nest components within other components:
function App() {
return {
tag: 'div',
attrs: { class: 'app' },
children: [
{
component: Header,
props: { title: 'My App' }
},
{
component: Card,
props: {
title: 'Welcome',
content: 'This is my first app using the Mini-Framework'
}
},
{
component: Footer,
props: { copyright: '2025' }
}
]
};
}The framework provides a useState hook for managing component state:
import { useState } from './framework/hooks.js';
function Counter() {
// Initialize state with default value 0
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
return {
tag: 'div',
children: [
{
tag: 'p',
children: [`Count: ${count}`]
},
{
tag: 'button',
attrs: {
onClick: increment
},
children: ['Increment']
}
]
};
}The useEffect hook lets you perform side effects:
import { useState, useEffect } from './framework/hooks.js';
function Timer() {
const [seconds, setSeconds] = useState(0);
// Effect runs after component renders
useEffect(() => {
const intervalId = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// Cleanup function runs before component unmounts
return () => {
clearInterval(intervalId);
};
}, []); // Empty dependency array means "run once on mount"
return {
tag: 'div',
children: [
{ tag: 'p', children: [`Seconds elapsed: ${seconds}`] }
]
};
}The dependency array controls when the effect runs:
- Empty array (
[]): Effect runs only after initial render - Omitted: Effect runs after every render
- With values: Effect runs when any dependency changes
The router allows navigation between different views:
import { initRouter, navigate } from './framework/router.js';
// Define routes
const routes = [
{
path: '/',
component: () => ({
tag: 'div',
children: [{ tag: 'h1', children: ['Home Page'] }]
})
},
{
path: '/about',
component: () => ({
tag: 'div',
children: [{ tag: 'h1', children: ['About Page'] }]
})
}
];
// Initialize the router with a container element
initRouter(routes, document.getElementById('root'));
// Navigation function
function Navigation() {
return {
tag: 'nav',
children: [
{
tag: 'a',
attrs: {
href: '/',
onClick: (e) => {
e.preventDefault();
navigate('/');
}
},
children: ['Home']
},
{
tag: 'a',
attrs: {
href: '/about',
onClick: (e) => {
e.preventDefault();
navigate('/about');
}
},
children: ['About']
}
]
};
}- When you create a component, it returns a virtual DOM tree (a JavaScript object representation of the DOM).
- The
renderfunction takes this virtual DOM tree and creates actual DOM elements. - When state changes, the component rerenders, creating a new virtual DOM tree.
- The framework compares the new virtual DOM with the previous one (diffing).
- Only the necessary changes are applied to the real DOM, making updates efficient.
- The
useStatehook creates a piece of state in the component. - When you call the setter function, the framework:
- Updates the state value
- Triggers a rerender of the component
- Maintains the state across renders
The diffing algorithm compares the old and new virtual DOM trees to determine what needs to change:
- If element types are different, replace the entire element
- If element types are the same, update the attributes
- If both have children, recursively diff the children
- Special handling for lists with keys to maintain identity
This approach ensures that DOM updates are minimal and efficient.
Events are handled by:
- Adding event listeners to the real DOM elements
- When events occur, the framework calls your handler functions
- Your handlers can update state, causing the UI to refresh
- Keep components small and focused on a single responsibility
- Use keys for lists to help the diffing algorithm track items efficiently
- State management: Keep state as close as possible to where it's used
- Immutability: Never directly modify state, always create new objects/arrays
- Clean up side effects: Always return cleanup functions from
useEffectwhen using resources like timers or event listeners
This Mini-Framework provides a lightweight yet powerful approach to building web applications. By leveraging concepts like the virtual DOM, components, and hooks, it offers an efficient and intuitive way to create interactive UIs without the overhead of larger frameworks.