你了解vue diff算法吗?原理解析

diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历。那么大家对diff算法吗有多少了解?下面本篇文章就来带大家深入解析下vue的diff算法,希望对大家有所帮助!

1.png

一、是什么

diff 算法是一种通过同层的树节点进行比较的高效算法。(学习视频分享:vue视频教程)

其有两个特点:

  • 比较只会在同层级进行, 不会跨层级比较
  • 在diff比较的过程中,循环从两边向中间比较

diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较

二、比较方式

diff整体策略为:深度优先,同层比较

  • 比较只会在同层级进行, 不会跨层级比较

2.png

  • 比较的过程中,循环从两边向中间收拢

3.png

下面举个vue通过diff算法更新的例子:

新旧VNode节点如下图所示:

4.png

第一次循环后,发现旧节点D与新节点D相同,直接复用旧节点D作为diff后的第一个真实节点,同时旧节点endIndex移动到C,新节点的 startIndex 移动到了 C

5.png

第二次循环后,同样是旧节点的末尾和新节点的开头(都是 C)相同,同理,diff 后创建了 C 的真实节点插入到第一次创建的 D 节点后面。同时旧节点的 endIndex 移动到了 B,新节点的 startIndex 移动到了 E

6.png

第三次循环中,发现E没有找到,这时候只能直接创建新的真实节点 E,插入到第二次创建的 C 节点之后。同时新节点的 startIndex 移动到了 A。旧节点的 startIndexendIndex 都保持不动

7.png

第四次循环中,发现了新旧节点的开头(都是 A)相同,于是 diff 后创建了 A 的真实节点,插入到前一次创建的 E 节点后面。同时旧节点的 startIndex 移动到了 B,新节点的startIndex 移动到了 B

8.png

第五次循环中,情形同第四次循环一样,因此 diff 后创建了 B 真实节点 插入到前一次创建的 A 节点后面。同时旧节点的 startIndex移动到了 C,新节点的 startIndex 移动到了 F

9.png

新节点的 startIndex 已经大于 endIndex 了,需要创建 newStartIdxnewEndIdx 之间的所有节点,也就是节点F,直接创建 F 节点对应的真实节点放到 B 节点后面

10.png

三、原理分析

当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图

源码位置:src/core/vdom/patch.js

function patch(oldVnode, vnode, hydrating, removeOnly) {     if (isUndef(vnode)) { // 没有新节点,直接执行destory钩子函数         if (isDef(oldVnode)) invokeDestroyHook(oldVnode)         return     }      let isInitialPatch = false     const insertedVnodeQueue = []      if (isUndef(oldVnode)) {         isInitialPatch = true         createElm(vnode, insertedVnodeQueue) // 没有旧节点,直接用新节点生成dom元素     } else {         const isRealElement = isDef(oldVnode.nodeType)         if (!isRealElement && sameVnode(oldVnode, vnode)) {             // 判断旧节点和新节点自身一样,一致执行patchVnode             patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)         } else {             // 否则直接销毁及旧节点,根据新节点生成dom元素             if (isRealElement) {                  if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {                     oldVnode.removeAttribute(SSR_ATTR)                     hydrating = true                 }                 if (isTrue(hydrating)) {                     if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {                         invokeInsertHook(vnode, insertedVnodeQueue, true)                         return oldVnode                     }                 }                 oldVnode = emptyNodeAt(oldVnode)             }             return vnode.elm         }     } }

商匡云商
Logo
注册新帐户
对比商品
  • 合计 (0)
对比
0
购物车