zwhu / blog

嘛,写 blog 也要遵守基本法。
MIT License
66 stars 2 forks source link

vdom(2.5) - diff #42

Open zwhu opened 6 years ago

zwhu commented 6 years ago
    <button id="update">update</button>
    <div id="root"></div>
        let vdom1 = {
            tag: 'div',
            props: {
                style: 'border: solid 2px red;'
            },
            children: [{
                tag: 'a',
                props: {
                    id: '1',
                    href: 'http://test.com',
                    target: '_blank'
                },
                children: [{ text: 'click!' }]
            }, {
                text: 'text!!'
            }, {
                tag: 'div',
                props: {
                    class: 'class1 class2',
                    'data-attr': 'hello'
                },
                children: [{ text: 'hahaha!' }, {
                    tag: 'br'
                }]
            }, {
                tag: 'br'
            }]
        }
        let vdom2 = {
            tag: 'div',
            props: {
                class: 'vdom2'
            },
            children: [{
                tag: 'a',
                props: {
                    id: '1',
                    href: 'http://test.com',
                    target: '_blank'
                },
                children: [{ text: 'click!!!' }]
            }, {
                tag: 'h2',
                children: [{ text: 'hahahah!' }]
            }, {
                tag: 'div',
                props: {
                    class: 'class1 class2',
                    'data-attr': 'hello'
                },
                children: [{ text: 'xxixii!' }, {
                    tag: 'br'
                }]
            }, {
                tag: 'br'
            }]
        }
        let prev
        function render(container, root) {

            function dfs(node, prev = {}) {
                let element
                if (node.tag) {
                    if (prev && prev.tag === node.tag) {
                        element = prev.raw
                        updateProps(element, node.props, prev.props)
                    } else {
                        element = createElement(node.tag, node.props)
                    }

                    node.children && renderChildren(node.children, prev.children, element)

                } else if (typeof node.text === 'string') {
                    element = createTextNode(node.text)
                }

                node.raw = element
                return node
            }

            function renderChildren(children = [], prevChildren = [], parentElement) {
                children.forEach((child, index) => {
                    let prevChild = null
                    if (prevChildren) prevChild = prevChildren[index]
                    if (prevChild) {
                        let newNode = dfs(child, prevChild)
                        if (prevChild.tag !== child.tag) {
                            parentElement.replaceChild(newNode.raw, prevChild.raw)
                        }
                    } else {
                        let newNode = dfs(child)
                        parentElement.appendChild(newNode.raw)
                    }
                })

                prevChildren.slice(children.length).forEach((child) => {
                    parentElement.removeChild(child.raw)
                })

            }

            dfs(root, prev)
            if (!prev) {
                prev = root
                container.appendChild(prev.raw)
            }
        }

        function createElement(tag, props = {}) {
            const element = document.createElement(tag)
            updateProps(element, props)
            return element
        }

        function updateProps(element, props = {}, oldProps = {}) {
            Object.keys(oldProps).forEach((key) => { element.removeAttribute(key) })
            Object.keys(props).forEach((key) => { element.setAttribute(key, props[key]) })
        }

        function createTextNode(text) {
            return document.createTextNode(text)
        }

        let fake1 = render(root, vdom1)

        update.addEventListener('click', () => {
            fake2 = render(root, vdom2)
        })

show