多图讲解vue3快速diff算法

vue3的diff算法名称是快速diff算法。是在vue2双端diff的基础上增加了最长递增子序列的逻辑。下面一起看下是如何实现的

整体核心思路:复用,能不动就不动,能少移动就少移动,直接创建或者直接删除。

变量说明:

c1 旧节点数组

c2 新节点数组

i 两个数组的前指针 同步向后移动 因此使用一个变量即可标识

e1 旧数组的尾指针 依次向前移动

e2 新数组的尾指针 依次向前移动

前处理

从前往后依次比较节点 直至节点不相等 源码:

css 复制代码
    // 1. sync from start
    // (a b) c
    // (a b) d e
    while (i <= e1 && i <= e2) {
      const n1 = c1[i]
      const n2 = (c2[i] = optimized
        ? cloneIfMounted(c2[i] as VNode)
        : normalizeVNode(c2[i]))
      if (isSameVNodeType(n1, n2)) {
        patch(
          n1,
          n2,
          container,
          null,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else {
        break
      }
      i++
    }

后处理

从后往前依次比较节点 直至节点不相等 源码:

css 复制代码
    // 2. sync from end
    // a (b c)
    // d e (b c)
    while (i <= e1 && i <= e2) {
      const n1 = c1[e1]
      const n2 = (c2[e2] = optimized
        ? cloneIfMounted(c2[e2] as VNode)
        : normalizeVNode(c2[e2]))
      if (isSameVNodeType(n1, n2)) {
        patch(
          n1,
          n2,
          container,
          null,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else {
        break
      }
      e1--
      e2--
    }

仅新增

假如变化前后的两组节点分别如下:仅需要处理新增即可 当i大于e1说明有新增节点 新增[i, e2]的节点内容

源码:

css 复制代码
    // 3. common sequence + mount
    // (a b)
    // (a b) c
    // i = 2, e1 = 1, e2 = 2
    // (a b)
    // c (a b)
    // i = 0, e1 = -1, e2 = 0
    if (i > e1) {
      if (i <= e2) {
        const nextPos = e2 + 1
        const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
        while (i <= e2) {
          patch(
            null,
            (c2[i] = optimized
              ? cloneIfMounted(c2[i] as VNode)
              : normalizeVNode(c2[i])),
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
          i++
        }
      }
    }

仅删除

假如变化前后的两组节点分别如下:仅需要处理删除即可 当i大于e2说明需要删除节点 删除[i, e1]的节点

源码:

css 复制代码
    // 4. common sequence + unmount
    // (a b) c
    // (a b)
    // i = 2, e1 = 2, e2 = 1
    // a (b c)
    // (b c)
    // i = 0, e1 = 0, e2 = -1
    else if (i > e2) {
      while (i <= e1) {
        unmount(c1[i], parentComponent, parentSuspense, true)
        i++
      }
    }

其他case(新增 删除 移动)

假如经过前面几轮比较后 当前的现状如图所示:

  1. 新建一个keyToNewIndexMap用于收集c2列表的key:index 到这个map中
typescript 复制代码
 const keyToNewIndexMap: Map<string | number | symbol, number> = new Map()
      for (i = s2; i <= e2; i++) {
        if (nextChild.key != null) {
          keyToNewIndexMap.set(nextChild.key, i)
        }
      }
  1. 遍历旧列表c1 在keyToNewIndexMap中找,如果找到了 说明可以被复用, 如果没找到说明当前节点在新列表中不存在 应该被删除。

  2. 在遍历的过程中需要创建一个newIndexToOldIndexMap的数组(叫map其实是一个数组)。数组初始化为0, 0代表节点新增。然后填充这个数组。先用旧的节点在keyToNewIndexMap中找到对应节点在新列表的下标 然后使用旧节点所在的位置 + 1 填充到newIndexToOldIndexMap对应的位置。

  3. 遍历过旧数组后删除的case 就已经被处理完毕了 ,同时我们得到了newIndexToOldIndexMap数组,记录的是新节点在旧列表中的相对顺序。然后newIndexToOldIndexMap中为 0 的就是需要新增的节点.

  4. 接下来就是找到需要移动的节点 这里我们需要让节点尽量少移动。所以寻找newIndexToOldIndexMap中的最长递增子序列。在最长子序列中的节点是不需要移动的。

当newIndexToOldIndexMap[i] === 0 说明节点是新增的 需要去挂载dom.

当newIndexToOldIndexMap[i] === increasingNewIndexSequence[j] 说明节点不需要移动。同时移动两个指针。

当newIndexToOldIndexMap[i] !== increasingNewIndexSequence[j] 说明节点需要移动

源码 patchKeyedChildren

最长递增子序列的生成方式

欢迎大家点赞收藏评论呦~

相关推荐
Jiaberrr14 分钟前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ2 小时前
html+css+js实现step进度条效果
javascript·css·html
程序员大金3 小时前
基于SpringBoot+Vue+MySQL的装修公司管理系统
vue.js·spring boot·mysql
john_hjy3 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd3 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
yanlele3 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
It'sMyGo3 小时前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
睡觉然后上课4 小时前
c基础面试题
c语言·开发语言·c++·面试