<div id="app"></div>
function createVNode(tag, props = {}, children = []) {
    return { tag, props, children }
}

function mount(vnode, container) { 
    const element = (vnode.element = document.createElement(vnode.tag))

    Object.entries(vnode.props || {}).forEach(([key, value]) => {
        element.setAttribute(key, value)
    })

    if (typeof vnode.children === 'string') {
        element.textContent = vnode.children
    } else {
        vnode.children.forEach(child => {
            mount(child, element) // Recursively mount the children
        })
    }

    container.appendChild(element)
}

function unmount(vnode) {
    vnode.element.parentNode.removeChild(vnode.element)
}

function patch(VNode1, VNode2) {
    // Assign the parent DOM element
    const element = (VNode2.element = VNode1.element);

    // Now we have to check the difference between the two vnodes

    // If the nodes are of different tags, assume that the whole content has changed.
    if (VNode1.tag !== VNode2.tag) {
        // Just unmount the old node and mount the new node
        mount(VNode2, element.parentNode)
        unmount(Vnode1)
    } else {
        // Nodes have same tags
        // So we have two checks remaining
        // - Props
        // - Children

        // I am not going to check the props for now because it would just miss the point. I might write a third article which contains the full implementation

        // Checking the children
        // If the new node has a string for children
        if (typeof VNode2.children == "string") {
            // If the two children are **strictly** different
            if (VNode2.children !== VNode1.children) {
                element.textContent = VNode2.children;
            }
        } else {
            // If the new node has an array of children
            // - The length of children is the same
            // - The old node has more children than the new one
            // - The new node has more children than the old one

            // Find out the lengths
            const children1 = VNode1.children;
            const children2 = VNode2.children;
            const commonLen = Math.min(children1.length, children2.length)

            // Recursively call patch for all the common children
            for (let i = 0; i < commonLen; i++) {
                patch(children1[i], children2[i])
            }

            // If the new node has fewer children
            if (children1.length > children2.length) {
                children1.slice(children2.length).forEach(child => {
                    unmount(child)
                })
            }

            // If the new node has more children
            if (children2.length > children1.length) {
                children2.slice(children1.length).forEach(child => {
                    mount(child, element)
                })
            }

        }
    }
}

// END OF VDOM ENGINE
//***********************************************************************************

function generateList(list) {
    let children = list.map(child => createVNode("li", null, child));

    return createVNode("ul", { class: 'fruits-ul' }, children)
}

let list = generateList(["apple", "banana", "orange"]);

mount(list, document.querySelector("#app"))

// Later
//****************

setTimeout(() => patch(list, generateList(["apple", "banana", "pineapple changed"])), 3000)

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.