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

相关推荐
胡gh2 小时前
页面卡成PPT?重排重绘惹的祸!依旧性能优化
前端·javascript·面试
ningqw2 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
你的人类朋友2 小时前
vi编辑器命令常用操作整理(持续更新)
后端
胡gh2 小时前
简单又复杂,难道只能说一个有箭头一个没箭头?这种问题该怎么回答?
javascript·后端·面试
言兴2 小时前
# 深度解析 ECharts:从零到一构建企业级数据可视化看板
前端·javascript·面试
山有木兮木有枝_2 小时前
TailWind CSS
前端·css·postcss
一只叫煤球的猫3 小时前
看到同事设计的表结构我人麻了!聊聊怎么更好去设计数据库表
后端·mysql·面试
uzong3 小时前
技术人如何对客做好沟通(上篇)
后端
烛阴3 小时前
TypeScript 的“读心术”:让类型在代码中“流动”起来
前端·javascript·typescript
杨荧3 小时前
基于Python的农作物病虫害防治网站 Python+Django+Vue.js
大数据·前端·vue.js·爬虫·python