Core Renderer Responsibilities
A renderer bridges virtual DOM (vDOM) nodes and platform-specific UI elements, integrating with reactivity systems to trigger targeted updates only when state changes. Its primary focus is minimizing DOM operations by pinpointing and acting on exact differences between previous and current vDOM trees.
Terminology Refresher Mounting converts a single vDOM tree into real UI elements and inserts them into a parent container. Patching compares an existing rendered tree (oldVNode) with an updated tree (newVNode) to apply granular changes. Unmounting removes all UI elements associated with a given vDOM subtree.
Core Implementation Scaffold
Initialize the renderer via a factory function to enable future cross-platform customization (e.g., switching DOM APIs for canvas or WebGL). The factory exposes a core render method that handles all three lifecycle actions.
function buildRenderer() {
// Future cross-platform adapter hooks will go here
function coreRender(currentVNode, rootContainer) {
// ...
}
return { coreRender };
}
This render method orchestrates mounting, patching, and unmounting based on input parameters:
const domRenderer = buildRenderer();
// Initial insertion
const appRoot = document.getElementById('app-root');
domRenderer.coreRender(firstVTree, appRoot);
// Granular update
domRenderer.coreRender(secondVTree, appRoot);
// Full subtree removal
domRenderer.coreRender(null, appRoot);
Since mounting can be simplified as patching with no previous tree, adjust coreRender accordingly. Store the last rendered vTree directly on the container for easy access during future updates.
function coreRender(currentVNode, rootContainer) {
if (currentVNode) {
diffAndApply(rootContainer._lastRendered, currentVNode, rootContainer, null);
} else {
if (rootContainer._lastRendered) {
removeSubtree(rootContainer._lastRendered);
}
}
rootContainer._lastRendered = currentVNode;
}
The diffAndApply (formerly patch) function splits work by presence of an existing tree:
function diffAndApply(oldNode, newNode, parentEl, insertBeforeNode) {
if (!oldNode) {
mountNewElement(newNode, parentEl, insertBeforeNode);
} else {
updateExistingElement(oldNode, newNode);
}
}
vDOM Structure and Basic Element Mounting
A standard vDOM element has three core fields: tag for node type, attrs for attributes/properties, and content which can be a plain string or array of child vNodes.
const sampleVTree = {
tag: 'section',
attrs: { class: 'content-wrapper', 'data-section-id': 'hero' },
content: [
{ tag: 'h1', content: 'Custom Renderer Demo' },
{ tag: 'p', content: 'Only updates modified nodes!' }
]
};
When mounting, create a real DOM element first, then assign attributes and populate content.
function mountNewElement(vdomNode, parentEl, insertAnchor) {
const realEl = document.createElement(vdomNode.tag);
vdomNode._associatedEl = realEl; // Link for later updates/unmounts
// Process attributes/properties
if (vdomNode.attrs) {
Object.entries(vdomNode.attrs).forEach(([key, value]) => {
applyAttribute(realEl, key, value);
});
}
// Process content
if (typeof vdomNode.content === 'string') {
realEl.textContent = vdomNode.content;
} else if (Array.isArray(vdomNode.content)) {
vdomNode.content.forEach(childNode => {
diffAndApply(null, childNode, realEl, null);
});
}
parentEl.insertBefore(realEl, insertAnchor);
}