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 是如何求得最优的移动方案的呢?这个,我们就留到下一回再说。

相关推荐
Chrikk13 分钟前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*16 分钟前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue16 分钟前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man19 分钟前
【go从零单排】go语言中的指针
开发语言·后端·golang
cs_dn_Jie19 分钟前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic1 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿1 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具2 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
qq_390161772 小时前
防抖函数--应用场景及示例
前端·javascript