Understanding Virtual DOM that powers ReactJS and VueJS
October 28, 2023You want to understand the main core concept behind React, VueJS ? You are in the right place ! Let’s dive into making our own virtual DOM using simple JavaScript.
Guide to make your own virtual DOM
To create a Virtual DOM, we need to understand the composition of the DOM structure, which consists of HTML nodes.
A HTML node is made of:
- Tag: div, span, p, h1,…
- Attributes: class, id, label, value,…
- Children nodes: contains zero, one or more nodes.
Now let’s create the virtual DOM using JavaScript. First we need to create a function representing the anatomy of a HTML node in a JSON structure.
function createVirtualNode(tag, attributes, children) {
return {
// div, span, p, h1,...
tag: tag || "",
// class, id, label, value,...
attributes: attributes || {},
children: children || [],
};
}
Now let’s determine the HTML structure we want to build:
<div id="header">
<p>Hello <span class="bold text-pink">everybody</span> !</p>
<img
src="https://images.unsplash.com/photo-1414235077428-338989a2e8c0"
alt="restaurant"
style="width: 300px;"
/>
</div>
Ok let’s use our function createVirtualNode
to represent this HTML structure.
Read and analyze the following carefully (yeah it’s hard to read).
// <div id="header"></div>
const virtualDOM = createVirtualNode("div", { id: "header" }, [
// <p>Hello</p>
createVirtualNode("p", {}, [
"Hello ",
// <span class="bold text-pink">everybody</span>
createVirtualNode("span", { class: "bold text-pink" }, ["everybody"]),
" !",
]),
// <img src="..." alt="restaurant" style="width: 300px;" />
createVirtualNode("img", {
src: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0",
alt: "restaurant",
style: "width: 300px;",
}),
]);
console.dir(virtualDOM, { depth: null });
Let’s see what our virtual DOM looks like:
// console.dir(virtualDOM, { depth: null })
{
tag: 'div',
attributes: { id: 'header' },
children: [
{
tag: 'p',
attributes: {},
children: [
'Hello ',
{
tag: 'span',
attributes: { class: 'bold text-pink' },
children: [ 'everybody' ]
},
' !'
]
},
{
tag: 'img',
attributes: {
src: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0',
alt: 'restaurant'
},
children: []
}
]
}
Wow… we just made our first virtual DOM JSON structure.
But what do we do now with this JSON structure now ? They are 2 big usages of this virtual DOM in the real world:
- Render it to the real DOM
- Server side rendering: Generating the corresponding HTML server-side and send an HTML page
Virtual DOM Client side: mounting into the real DOM
Let’s render it to the real DOM first by creating the render
function:
function renderDOMNode({ tag, attributes, children }) {
const node = document.createElement(tag);
for (const attributeKey in attributes) {
node.setAttribute(attributeKey, attributes[attributeKey]);
}
for (const childrenNode of children) {
if (typeof childrenNode === "string") {
node.appendChild(document.createTextNode(childrenNode));
} else {
node.appendChild(renderDOMNode(childrenNode));
}
}
return node;
}
Let’s use it !
<html>
<head>
<title>Virtual DOM rendered client-side</title>
</head>
<body>
<div id="app" />
</body>
</html>
const node = renderVirtualNode(virtualDOM);
document.getElementById("app").appendChild(node);
Virtual DOM Server side: render to string for Server side rendering (SSR)
function renderVirtualNodeToString({ tag, attributes, children }) {
let node = `<${tag}`;
for (const attributeKey in attributes) {
node += ` ${attributeKey}="${attributes[attributeKey]}"`;
}
if (children.length === 0) {
node += "/>";
} else {
node += ">";
for (const childrenNode of children) {
if (typeof virtualChildNode === "string") {
node += virtualChildNode;
} else {
node += renderVirtualNodeToString(childrenNode);
}
}
node += `</${tag}>`;
}
return node;
}
Now let’s see how to use our VirtualDOM server side using NodeJS:
import http from "http";
const renderedHTML = renderVirtualNodeToString(virtualDOM);
const server = http.createServer((request, response) => {
response.writeHead(200, { "Content-Type": "text/html" });
response.end(`
<html>
<head>
<title>Virtual DOM rendered server-side</title>
</head>
<body>
<div id="app">${renderedHTML}</div>
</body>
</html>
`);
});
const PORT = 3000;
server.listen(PORT, () => {
console.info(`Server is running at http://localhost:${PORT}`);
});
What you see above demonstrates the power and flexibility of a Virtual DOM. By maintaining a representation of the DOM in memory, frameworks like React and Vue can optimize updates and perform advanced features such as server-side rendering, which allows for improved performance and SEO benefits.