多图讲解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中的最长递增子序列。在最长子序列中的节点是不需要移动的。

当newIndexToOldIndexMapi === 0 说明节点是新增的 需要去挂载dom.

当newIndexToOldIndexMapi === increasingNewIndexSequencej 说明节点不需要移动。同时移动两个指针。

当newIndexToOldIndexMapi !== increasingNewIndexSequencej 说明节点需要移动

源码 patchKeyedChildren

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

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

相关推荐
陈拉斯5 分钟前
给公司年会写了个大屏抽奖系统:动画在前端跑,凭什么说结果没被改?
javascript
daols887 分钟前
vue 甘特图组件 vxe-gantt 自动计算工期显示关键路径
vue.js·vxe-gantt
—Qeyser26 分钟前
Element Plus 安装与配置指南 (Vue 3 + Vite + TS)
vue.js
雪芽蓝域zzs28 分钟前
第19章:ECharts 组合图(折线 + 柱状混合图,双 Y 轴)
vue.js·echars
yzy8537 分钟前
Vue中下载或打开文件的JavaScript方法
前端·javascript·vue
小刘在重生~1 小时前
Oracle_day2 单行函数|组函数|分组|伪列|子查询|分页|多表连接
笔记·oracle·面试
用户298698530141 小时前
在 React 中用 JavaScript 将 Word 转换为文本
前端·javascript·react.js
Sam_Deep_Thinking1 小时前
关于java final关键字的可见性
java·后端·面试·程序员
雪芽蓝域zzs1 小时前
第17章:ECharts 饼图自定义样式(扇区颜色、轮廓、标签配置 + 空状态兼容)
vue.js·echars
雪芽蓝域zzs1 小时前
第15章:ECharts 图表事件交互(若依 Vue3)
vue.js·echars