-
-
Save FocusThen/5ba921b77051eb9a136008793e821cc7 to your computer and use it in GitHub Desktop.
Vue.js-like Virtual DOM
This file contains 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 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