Vue3中的diff算法——diff算法需要处理的几种场景

大家好,我是哈默。今天我们来继续来学习一下 vue3 中的 diff算法。上回,我们说到了什么时候需要使用 diff 算法。在明确了 diff 算法使用的前提之后,我们再来看下 diff算法 需要处理哪些场景呢?

两组元素个数相同的时候

首先,我们先来看下元素个数相同的时候,比如:

旧节点,ul 元素下是一组 li,总共 3 个 li

js 复制代码
<ul>
  <li>a</li>
  <li>b</li>
  <li>c</li>
</ul>

新节点,ul 元素下也是一组 li,总共也是 3 个 li

js 复制代码
<ul>
  <li>a</li>
  <li>b</li>
  <li>d</li> // 第三个 li 元素发生了变化!👾
</ul>

这个时候,我们只需要遍历两组子节点,然后依次更新每一个节点就可以了,代码如下:

js 复制代码
const oldChildren = ul1.children;
const newChildren = ul2.children;

for (let i = 0; i < oldChildren.length; i++) {
  // 调用 patch 函数依次更新子节点
  patch(oldChildren[i], newChildren[i]);
}

两组元素个数不同的时候

但在实际情况中,我们新的一组元素常常和旧的一组元素个数不一样。

这个时候,就会有 2 种情况:

  1. 新的一组元素数量 > 旧的一组元素数量
  2. 新的一组元素数量 < 旧的一组元素数量

这个时候,我们可以首先获取两组子节点公共的元素数量的长度,然后如果新的一组元素多,则挂载剩余的新元素;新的一组元素少,则卸载旧元素即可。代码如下:

js 复制代码
const oldChildren = ul1.children;
const newChildren = ul2.children;

const oldChildrenLength = oldChildren.length;
const newChildrenLength = newChildren.length;

// 获取公共的长度
const commonLength = Math.min(oldChildrenLength, newChildrenLength);

for (let i = 0; i < commonLength; i++) {
  patch(oldChildren[i], newChildren[i]);
}

// 新的一组元素多,则挂载剩余的新元素
if (newChildrenLength > oldChildrenLength) {
  for (let i = commonLength; i < newChildrenLength; i++) {
    patch(null, newChildren[i]);
  }
}
// 新的一组元素少,则卸载旧元素即可
else if (newChildrenLength < oldChildrenLength) {
  for (let i = commonLength; i < oldChildrenLength; i++) {
    unmount(oldChildren[i]);
  }
}

两组元素的顺序不同

还有一种更加复杂,但是也是很常见的情况,就是新的一组元素的顺序和旧的一组元素的顺序是不同的,比如:

旧节点:

js 复制代码
[
  { type: "p", children: "我是p1" },
  { type: "p", children: "我是p2" },
  { type: "p", children: "我是p3" },
];

新节点:

js 复制代码
[
  { type: "p", children: "我是p2" },
  { type: "p", children: "我是p3" },
  { type: "p", children: "我是p1" },
];

这个时候,最高效的更新节点的方式是:将 我是p1 移动到新的一组节点的末尾即可。

但是,这对于我们肉眼来说,是很简单的。但程序怎么知道谁是 p1 呢?

因为我们现在只用了 type 这个属性来区分不同的节点,这个属性的值可能是 p、div、li...

所以,我们需要引入一个新的属性来区分相同的 typep 的节点,这个新属性就是 key

旧节点:

js 复制代码
[
  { type: "p", children: "我是p1", key: 1 },
  { type: "p", children: "我是p2", key: 2 },
  { type: "p", children: "我是p3", key: 3 },
];

新节点:

js 复制代码
[
  { type: "p", children: "我是p2", key: 2 },
  { type: "p", children: "我是p3", key: 3 },
  { type: "p", children: "我是p1", key: 1 },
];

这个时候,我们就很清晰的知道:对于 p1 来说,它从旧节点的第一位,移动到了新节点的第三位。

总结

那么今天,我们一共探讨了diff算法 需要处理的 3 种不同的情况。

但现在,我们只是从理论的角度进行了分析,但 vue3 中,它具体是如何实现这个 diff算法 的呢?

比如,我们这里最后说的 移动节点,我们可以把 p1 移动到最后,也可以把 p2p3 往前移动,那么 vue3 是如何求得最优的移动方案的呢?这个,我们就留到下一回再说。

相关推荐
程序员码歌25 分钟前
短思考第261天,浪费时间的十个低效行为,看看你中了几个?
前端·ai编程
Swift社区1 小时前
React Navigation 生命周期完整心智模型
前端·react.js·前端框架
若梦plus1 小时前
从微信公众号&小程序的SDK剖析JSBridge
前端
掘金码甲哥1 小时前
🚀糟糕,我实现的k8s informer好像是依托答辩
后端
GoGeekBaird2 小时前
Andrej Karpathy:2025年大模型发展总结
后端·github
用泥种荷花2 小时前
Python环境安装
前端
Light602 小时前
性能提升 60%:前端性能优化终极指南
前端·性能优化·图片压缩·渲染优化·按需拆包·边缘缓存·ai 自动化
uzong2 小时前
听一听技术面试官的心路历程:他们也会有瓶颈,也会表现不如人意
后端
Jimmy2 小时前
年终总结 - 2025 故事集
前端·后端·程序员
烛阴2 小时前
C# 正则表达式(2):Regex 基础语法与常用 API 全解析
前端·正则表达式·c#