Last active
August 22, 2024 04:54
-
-
Save themarcba/2a471e1f0592201fc5036cb40bdbee1f to your computer and use it in GitHub Desktop.
Vue.js-like Virtual DOM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="app"></div> | |
<script> | |
// Create virtual node | |
function h(tag, props, children) { | |
// Return the virtual node | |
return { | |
tag, | |
props, | |
children, | |
} | |
} | |
// Mount a virtual node to the DOM | |
function mount(vnode, container) { | |
// Create the element | |
const el = (vnode.el = document.createElement(vnode.tag)) | |
// Set properties | |
for (const key in vnode.props) { | |
el.setAttribute(key, vnode.props[key]) | |
} | |
// Handle children | |
if (typeof vnode.children === 'string') { | |
el.textContent = vnode.children | |
} else { | |
vnode.children.forEach(child => { | |
mount(child, el) | |
}) | |
} | |
// Mount to the DOM | |
container.appendChild(el) | |
} | |
// Unmount a virtual node from the DOM | |
function unmount(vnode) { | |
vnode.el.parentNode.removeChild(vnode.el) | |
} | |
// Take 2 virtual nodes, compare & figure out what's the difference | |
function patch(n1, n2) { | |
const el = (n2.el = n1.el) | |
// Case where the nodes are of different tags | |
if (n1.tag !== n2.tag) { | |
mount(n2, el.parentNode) | |
unmount(n1) | |
} | |
// Case where the nodes are of the same tag | |
else { | |
// New virtual node has string children | |
if (typeof n2.children === 'string') { | |
el.textContent = n2.children | |
} | |
// New virtual node has array children | |
else { | |
// Old virtual node has string children | |
if (typeof n1.children === 'string') { | |
el.textContent = '' | |
n2.children.forEach(child => mount(child, el)) | |
} | |
// Case where the new vnode has string children | |
else { | |
const c1 = n1.children | |
const c2 = n2.children | |
const commonLength = Math.min(c1.length, c2.length) | |
// Patch the children both nodes have in common | |
for (let i = 0; i < commonLength; i++) { | |
patch(c1[i], c2[i]) | |
} | |
// Old children was longer | |
// Remove the children that are not "there" anymore | |
if (c1.length > c2.length) { | |
c1.slice(c2.length).forEach(child => { | |
unmount(child) | |
}) | |
} | |
// Old children was shorter | |
// Add the newly added children | |
else if (c2.length > c1.length) { | |
c2.slice(c1.length).forEach(child => { | |
mount(child, el) | |
}) | |
} | |
} | |
} | |
} | |
} | |
// Create virtual nodes & render them below this line π | |
// Virtual node 1 | |
const node1 = h('div', { class: 'container' }, [ | |
h('h1', null, 'Hello World π'), | |
h('p', null, 'Thanks for reading the marc.dev blog π'), | |
]) | |
// Mount the node to the DOM | |
mount(node1, document.getElementById('app')) | |
// Virtual node 2 | |
const node2 = h('div', { class: 'container' }, [ | |
h('h1', null, 'Hello Dev π»'), | |
h('p', null, [ | |
h('span', null, 'Thanks for reading the '), | |
h('a', { href: 'https://marc.dev' }, 'marc.dev'), | |
h('span', null, ' blog'), | |
]), | |
h( | |
'img', | |
{ | |
src: 'https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif', | |
style: 'width: 350px; border-radius: 0.5rem;', | |
}, | |
[], | |
), | |
]) | |
// Patch the new node to the old node | |
setTimeout(() => { | |
patch(node1, node2) | |
}, 3000) | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="app"></app> | |
<script> | |
// Create virtual node | |
function h(tag, props, children) { | |
// Return the virtual node | |
} | |
// Mount a virtual node to the DOM | |
function mount(vnode, container) { | |
// Create the element | |
// Set props | |
// Handle children | |
// Mount to the DOM | |
} | |
// Unmount a virtual node from the DOM | |
function unmount(vnode) { | |
// Unmount the virtual node | |
} | |
// Take 2 vnodes, compare & figure out what's the difference | |
function patch(n1, n2) { | |
// Case where the nodes are of different tags | |
// Case where the nodes are of the same tag | |
// Case where the new vnode has string children | |
// Case where the new vnode has an array of vnodes | |
} | |
// Create virtual nodes & render them below this line... | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is this code licensed under an open-source license or is it public domain? I'd like to use it in my project, but not sure about the licensing and copyright.