Vue 进阶 (六)

发布时间:2019-05-26 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Vue 进阶 (六)脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

数据更新时的 diff 和 patch 机制

一、数据视图更新

在当响应式数据发生变化的时候,就会触发setter ,对应的Dep中的watcher也会触发,watcher对象就会调用update方法,最终将新产生的VNode节点和老的VNode节点进行一个patch过程,对比得到差异之后,最终将这些差异更新到视图上。

二、 patch

首先说一下 patch 的核心 diff 算法,我们用 diff 算法可以比对出两颗树的「差异」

Vue 进阶 (六)

diff 算法是通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式,所以时间复杂度只有 O(n),是一种相当高效的算法,如下图。

Vue 进阶 (六)

function patch (oldVNode, vnode, parentElm) {
    if (!oldVnode) {
        // 如果不存在旧节点,就相当于是在新的节点替代原本没有的节点
        addVnodes(parentElm, null, vnode, 0, vnode.length - 1);
    } else if (!vnode) {
        // 如果新的节点中没有,那么就相当于是删除了原来的节点
        removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);
    } else {
        // 如果都存在
        if (sameVnode(oldVNode, vnode)) {
            // 新旧的节点是相同
            patchVnode(oldVNode, vnode);
        } else {
            // 新旧的节点是不相同的
            // 删除老节点,增加新节点。
            removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);
            addVnodes(parentElm, null, vnode, 0, vnode.length - 1);
        }
    }
}
同行比对

通过一个例子来模拟一下:

Vue 进阶 (六)

在设置好状态后,我们开始第一遍比较,此时oldStartVnode=a,`newStartVnode=a;命中了sameVnode(oldStartVnode,newStartVnode)逻辑,则直接调用patchVnode(oldStartVnode,newStartVnode,insertedVnodeQueue)方法更新节点a,接着把oldStartIdxnewStartIdx`索引分别+1,

Vue 进阶 (六)

更新完节点a后,我们开始第2遍比较,此时oldStartVnode=b,newEndVnode=b;命中了sameVnode(oldStartVnode,newEndVnode)逻辑,则调用patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)方法更新节点b,接着调用canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)),把节点b移到树的最右边,最后把oldStartIdx索引+1,newEndIdx索引-1,如图:

Vue 进阶 (六)

更新完节点b后,我们开始第三遍比较,此时oldEndVnode=d,newStartVnode=d;命中了sameVnode(oldEndVnode, newStartVnode)逻辑,则调用patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)方法更新节点d,接着调用canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm),把d移到c的左边。最后把oldEndIdx索引-1,newStartIdx索引+1,如图:

Vue 进阶 (六)

更新完d后,我们开始第4遍比较,此时newStartVnode=e,节点e在旧树里是没有的,因此应该被作为一个新的元素插入,调用createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm),后面执行了nodeOps.insertBefore(parent, elm, ref)方法把e插入到c之前,接着把newStartIdx索引+1,如图:

Vue 进阶 (六)

插入节点e后,我们可以看到newStartIdx已经大于newEndIdx了,while循环已经完毕。接着调用removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) 删除旧的c,最终如图:

Vue 进阶 (六)

updateChildren通过以上几步操作完成了旧树子节点的更新,实际上只用了比较小的dom操作,在性能上有所提升,并且当子节点越复杂,这种提升效果越明显。vnode通过patch方法生成dom后,会调用mounted hook,至此,整个vue实例就创建完成了,当这个vue实例的watcher观察到数据变化时,会两次调用render方法生成新的vnode,接着调用patch方法对比新旧vnode来更新dom.

脚本宝典总结

以上是脚本宝典为你收集整理的Vue 进阶 (六)全部内容,希望文章能够帮你解决Vue 进阶 (六)所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:Vue