diff算法

vue2中规定模板中必须放一个标签

vue3中可以放文本,因为在最外层常见了一个根节点

1、如果标签名称不一样,直接删掉老的换成新的即可

// 如果标签名称不一样 直接删掉老的换成新的即可
if (oldVnode.tag !== vnode.tag) {
    // 可以通过vnode.el属性。获取现在真实的dom元素
    return oldVnode.el.parentNode.replaceChild(createElm(vnode), oldVnode.el);
}

2、 如果两个虚拟节点是文本节点,比较文本内容

        if (vnode.tag == undefined) { // 新老都是文本
            if (oldVnode.text !== vnode.text) {
                el.textContent = vnode.text;
            }
            return;
        }

3、如果标签一样,比较属性

  • 直接用新的属性生成到元素上
  • 如果老的有属性,新的没有直接删除
  • 删除样式中老的有,新的没有的
function patchProps(vnode, oldProps = {}) { // 初次渲染时可以调用此方法,后续更新也可以调用此方法
    let newProps = vnode.data || {};
    let el = vnode.el;

    // 如果老的属性有,新的没有直接删除
    let newStyle = newProps.style || {};
    let oldStyle = oldProps.style || {};
    for (let key in oldStyle) {
        if (!newStyle[key]) { // 新的里面不存在这个样式
            el.style[key] = '';
        }
    }
    for (let key in oldProps) {
        if (!newProps[key]) {
            el.removeAttribute(key);
        }
    }

    // 直接用新的生成到元素上
    for (let key in newProps) {
        if (key === 'style') {
            for (let styleName in newProps.style) {
                el.style[styleName] = newProps.style[styleName];
            }
        } else {
            el.setAttribute(key, newProps[key]);
        }
    }
}

4、一方有儿子,一方没儿子

  • 老的没儿子,新的有儿子
            for (let i = 0; i < newChildren.length; i++) {
                let child = createElm(newChildren[i]);
                el.appendChild(child); // 循环创建新节点
            }
  • 老的有儿子,新的没儿子
el.innerHTML = ``; // 直接删除老节点

 5、双方都有儿子

  • 节点相同,只是属性变化,同时循环新的节点和 老的节点,有一方循环完毕就结束了

    while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
        // 同时循环新的节点和 老的节点,有一方循环完毕就结束了
        if (isSameVnode(oldStartVnode, newStartVnode)) { // 头头比较,发现标签一致,
            patch(oldStartVnode, newStartVnode);
            oldStartVnode = oldChildren[++oldStartIndex];
            newStartVnode = newChildren[++newStartIndex];
        }
    }
  •  追加元素,直接插入到元素中

    // 如果用户追加了一个怎么办?

    // 这里是没有比对完的
    if (newStartIndex <= newEndIndex) {
        for (let i = newStartIndex; i <= newEndIndex; i++) {
            el.appendChild(createElm(newChildren[i]))
        }
    }
  • 头部插入新元素,倒序比较

    while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
        // 同时循环新的节点和 老的节点,有一方循环完毕就结束了
        if (isSameVnode(oldStartVnode, newStartVnode)) { // 头头比较,发现标签一致,
            patch(oldStartVnode, newStartVnode);
            oldStartVnode = oldChildren[++oldStartIndex];
            newStartVnode = newChildren[++newStartIndex];
        }else if(isSameVnode(oldEndVnode,newEndVnode)){ // 从尾部开始比较
            patch(oldEndVnode,newEndVnode);
            oldEndVnode = oldChildren[--oldEndIndex];
            newEndVnode = newChildren[--newEndIndex];
        }
    }


    // 这里是没有比对完的
    if (newStartIndex <= newEndIndex) {
        for (let i = newStartIndex; i <= newEndIndex; i++) {
            // el.appendChild(createElm(newChildren[i]))
            // insertBefore方法 他可以appendChild功能 insertBefore(节点,null)  dom api

            //  看一下伪指针的下一个元素是否存在
            let anchor = newChildren[newEndIndex + 1] == null? null :newChildren[newEndIndex + 1].el
            el.insertBefore(createElm(newChildren[i]),anchor);
        }
    }
  • 删除尾部节点

    if(oldStartIndex <= oldEndIndex){
        for (let i = oldStartIndex; i <= oldEndIndex; i++) {
            el.removeChild(oldChildren[i].el);
        }
    } 
  • 节点反转,用key比较,交叉比对
    • 先做头尾比较

头和头比不一样,尾和尾比不一样。老A和新A比较,发现一样,放到D的后面,头部老指针向后移到B,尾部新指针向前移到B。

头和头比不一样,尾和尾比不一样。老B和新B一样,把B移到D的后面,头部老指针向后移,尾部新指针向前移

 

头和头比不一样,尾和尾比不一样。老C和新C一样,把C移到D的后面,头部老指针向后移,尾部新指针向前移。

头和头比较,一样结束

 // 头尾比较  =》 reverse
        else if(isSameVnode(oldStartVnode,newEndVnode)){
            patch(oldStartVnode,newEndVnode);
            el.insertBefore(oldStartVnode.el,oldEndVnode.el.nextSibling); // 移动老的元素,老的元素就被移动走了,不用删除
            oldStartVnode = oldChildren[++oldStartIndex];
            newEndVnode = newChildren[--newEndIndex];
        }
  •  再做尾头比较

头头比较不一样,尾尾比较不一样,头尾比较不一样,尾头比较一样 ,把D插入到A前面

        else if(isSameVnode(oldEndVnode,newStartVnode)){ // 尾头比较
            patch(oldEndVnode,newStartVnode);
            el.insertBefore(oldEndVnode.el,oldStartVnode.el);
            oldEndVnode = oldChildren[--oldEndIndex];
            newStartVnode = newChildren[++newStartIndex];
        }

 6、乱序比较

根据key和对应的索引将老的内容生成映射表

将第一个元素B去映射表中去查找, 如果找到,把这个元素放到前面,原位置置NULL

相同之后指针同时往后移

 头尾比较发现一样,把A放到D的后面并且指针后移,发现是NULL跳过

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值