diff算法

createElemnet生成Elemnet对象

//封装createElement
class Element {
    constructor(type,props,children){
        this.type=type;
        this.props=props;
        this.children=children;
    }
};
//返回object的
function createElement(type,props,children){
    return new Element(type,props,children);
};

render将对象渲染成真实DOM

//将虚拟dom转化成真实DOM
function render(eleObj){
    let el=document.createElement(eleObj.type);
    for(let key in eleObj.props){
        //设置属性的方法
        setAttr(el,key,eleObj.props[key])
    }
    //遍历儿子 如果是虚拟DOM 就继续渲染,如果不是就代表是文本节点
    eleObj.children.forEach((child)=>{
        console.log(child)
        if(child instanceof Element){
            child=render(child);
        }else{
            child=document.createTextNode(child)
        }
        el.appendChild(child)
    })
    return el;
}
let vertual=render(vertualDom)
console.log()

//将虚拟DOM转化成的真实DOM 绑定到页面上
function renderDom(el,target){
    target.appendChild(el)
}
renderDom(vertual,document.querySelector('#app'))

diff的比较规则

1.比较属性是否相同 {type:'ATTRS',attrs:{class:'aa'}}
2.新的DOM节点不存在{type:'REMOVE',index:3}
3.节点类型不相同 直接采用替换的模式{type:'REPLACE',newNode:newNode}
4.文本的变化{type:'TEXT',text:1}

diff比较两个Elemnet对象,生成补丁

const ATTRS='ATTRS';
const TEXT='TEXT';
const REMOVE='REMOVE';
const REPLACE='REPLACE';
let Index=0;
function diff(oldTree,newTree){
    let patches={};
    let index=0;
    //递归树,比较后的结果放到补丁包中
    walk(oldTree,newTree,index,patches)

    return patches
}
function walk(oldTree,newTree,index,patches){
    let currentPatch=[];
    if(!newTree){//新节点被删除
        currentPatch.push({type:'REMOVE',index:index})
    }else if((typeof oldTree == 'string')&& (typeof newTree == 'string')){//新节点是文本
        if(oldTree !== newTree){//判断文本是不是发生变化了
            currentPatch.push({type:'TEXT',text:newTree})
        }
    }else if(oldTree.type==newTree.type){//如果节点相同就比属性
            //比较属性是否有更改
            let attrs=diffAttr(oldTree.props,newTree.props)
            if(Object.keys(attrs).length>0){//说明属性有变化
                currentPatch.push({type:ATTRS,attrs:attrs})
            }
            //如果有儿子节点 遍历儿子节点
            diffChildren(oldTree.children,newTree.children,index,patches);
    }else{//节点不同
        currentPatch.push({type:'REPLACE',newNode:newTree})
    }
    if(currentPatch.length>0){//当前元素确实有补丁
        //将元素和补丁对应起来 放到大补丁中
        patches[index]=currentPatch
    }
}

function diffAttr(oldAttrs,newAttrs){
    let patch={};
    for(let key in oldAttrs){
        //老节点的属性发生变化
        if(oldAttrs[key]!==newAttrs[key]){
            patch[key]=newAttrs[key]//有可能是undefined
        }
    }
    for(let key in newAttrs){
        //老节点没有新节点的属性
        if(!oldAttrs.hasOwnProperty(key)){
            patch[key]=newAttrs[key]
        }
    }
    return patch
}
function diffChildren(oldChildren,newChildren,index,patches){
    //比较老的第一个和新的第一个
    oldChildren.forEach((child,idx)=>{
        console.log(++index,204)
        walk(child,newChildren[idx],++Index,patches)
    })
}

let patches=diff(vertualDom1,vertualDom2)//得到两棵DOM树的补丁包
console.log(patches)

patch打补丁,重新渲染真实DOM

let index=0;//默认哪个需要打补丁

function patch(node , patches){
    console.log(node)
    //给某个元素打补丁
    console.log(patches)
    walkpat(node,patches)
}
function walkpat(node,patches){
    console.log(patches)
    let currentPatch=patches[index++];
    let childNodes=node.childNodes;
    childNodes.forEach((child)=>{
        walkpat(child,patches);
    })
    if(currentPatch&¤tPatch.length>0){
        doPach(node,currentPatch)
    }
}
function doPach(node,patches){
    patches.forEach(patch=>{
        switch(patch.type){
            case 'ATTRS':
                for(let key in patch.attrs){
                    let value=patch.attrs[key]
                    if(value){
                        setAttr(node,key,value);
                    }else{
                        node.removeAttribute(key);
                    }
                }
                patch.attrs
                break;
            case 'TEXT':
                node.textContent=patch.text;
                break;
            case 'PEPLACE':
                let newNode=patch.newNode instanceof Element?render(patches.newNode):document.createTextNode(patch.newNode)
                break;
            case 'REMOVE':
                node.parentNode.removeChild(node)
                break;
            default:
                break;
        }
    })
}

patch(vertual,patches)

封装的缺陷

1.如果凭借元素有互换,那么会导致重新渲染
2.新增节点不会更新

你可能感兴趣的:(diff算法)