简单renderer函数

83 阅读1分钟

html引入renderer

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
    <script src="./renderer.js"></script>
    <script>
        const vnode = h('div',{},[
            h('div',{class:'yui'},'yui'),
            h('span',{calss:'span'},'span')
        ])
        mount(vnode,document.querySelector('#app'))
    </script>
</body>
</html>

js的renderer

const h = (tag, props, children) =>{
    return{
        tag,
        props,
        children
    }
}

const mount = (vnode,container) =>{
    const el = vnode.el = document.createElement(vnode.tag)
    if(vnode.props){
        for (const key in vnode.props) {
            const value = vnode.props[key]
            if(key.startsWith('on')){
                el.addEventListener(key.slice(2).toLowerCase(),value)
            }else{
                el.setAttribute(key,value)
            }
        }
    }
    if(vnode.children){
        if(typeof vnode.children === 'string'){
            el.textContent = vnode.children;
        }else{
            vnode.children.forEach(element => {
                mount(element,el)
            });
        }
    }
    container.appendChild(el)
}

const patch = (n1,n2) => {
    if(n1.tag !== n2.tag){
        const n1Elparenter = n1.el.parentElement;
        n1Elparenter.removeChild(n1.el);
        mount(n2,n1Elparenter)
    }else{
        const el = n1.el = n2.el;
        const oprops = n1.props || {};
        const nprops = n2.props || {};
        for (const key in nprops) {
            const ovalue = oprops[key];
            const nvalue = nprops[key];
            if(ovalue !== nvalue){
                if(key.startsWith('on')){
                    el.addEventListener(key.slice(2).toLowerCase(),nvalue)
                }else{
                    el.setAttribute(key,nvalue)
                }
            }
        }
        for (const key in oprops) {
            if(!(key in nprops)){
                if(key.startsWith('on')){
                    const value = oprops[key]
                    el.removeEventListener(key.slice(2).toLowerCase(),value)
                }else{
                    el.removeAttribute(key)
                }
            }
        }

        //children
        const newchildren = n1.children || {};
        const oldchildren = n2.children || {};
        if(typeof newchildren === 'string'){
            if(typeof oldchildren === 'string'){
                if(newchildren !== oldchildren){
                    el.textContent = newchildren;
                }
            }else{
                el.innerHtml = newchildren;
            }
        }else{
            if(typeof oldchildren === 'string'){
                el.textContent = ""
                newchildren.forEach(item =>{
                    mount(item,el)
                })
            }else{
                const min = Math.min(oldchildren.length,newchildren.length)
                for (let i = 0; i < min; i++) {
                    patch(oldchildren[i],newchildren[i])
                }
                if(oldchildren.length > newchildren.length){
                    oldchildren.length.slice(min).forEach(item=>{
                        el.removeChild(item)
                    })
                }
                if(newchildren.length > oldchildren.length){
                    newchildren.length.slice(min).forEach(item=>{
                        mount(item,el)
                    })
                }
            }
        }
    }
}