vue 优化的 diff 策略

diff 调用 patch 函数,patch 接收两个参数 vnode, oldVnode, 分别代表新旧节点

function patch (oldVnode, vnode) {
    if (sameVnode(oldVnode, vnode)) {
        patchVnode(oldVnode, vnode)
    } else {
        const oEl = oldVnode.el
        let parentEle = api.parentNode(oEl)
        createEle(vnode)
        if (parentEle !== null) {
            api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl))
            api.removeChild(parentEle, oldVnode.el)
            oldVnode = null
        }
    }
    return vnode
}

patch函数内第一个 if 判断sameVnode(oldVnode, vnode) 就是判断这两个节点是否为同一类型节点.


function sameVnode(oldVnode, vnode){
  //两节点key值相同,并且sel属性值相同,即认为两节点属同一类型,可进行下一步比较
    return vnode.key === oldVnode.key && vnode.sel === oldVnode.sel
}

即便是同一个节点, 他的 className 不同, vue 就认为是两个不同类型的节点,执行删除旧节点\插入新节点操作,这和 react diff 实现不同, react 对于同一个节点元素认为是同一个类型节点,只更新其节点上的属性.

patchVnode

过程可以概括为:oldCh 和newCh 各有两个头尾的变量 StartIdx 和 EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较。

这种由两端至中间的对比方法与react的updateChildren实现也是不同,后者是从左至右依次进行对比,各有优点。 比如一个集合,只是把最后一个节点移到了第一个,react实现就出现了短板,react会依次移动前三个节点到对应的位置:

最后更新于

这有帮助吗?