从Vue3源码中分析key的作用

在文档中说到:

  • key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes
  • 如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。
  • 而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除/销毁 key 不存在的元素。

在分析之前需要简单了解俩个概念 Vnode虚拟 DOM

需要了解的俩个概念

Vnode

Vnode 全称为 Virtual Node, 即 虚拟节点 ,而它的本质就是一个 JS 对象

以代码为例:

1
<div class="text" style="font-size: 26px;color: pink">这是一段文本内容</div>

// vue 会把 模板中的内容 转换为 vNode(对象)

1
2
3
4
5
6
7
8
9
10
11
const vNode = {
type: 'div',
props: {
class: 'text',
style: {
'font-size': '26px',
color: 'pink',
},
},
children: '这是一段文本内容',
}

虚拟 DOM

文档中说到:

  • 虚拟 DOM 是轻量级的 JavaScript 对象,由渲染函数创建。
  • 它包含三个参数:元素,具有数据、prop、attr 等的对象,以及一个数组。
  • 数组是我们传递子级的地方,子级也具有所有这些参数,然后它们也可以具有子级,依此类推,直到我们构建完整的元素树为止。

以代码为例:

1
2
3
4
5
<div>
<h1>标题</h1>
这是一段文本内容
<!-- 这是注释 -->
</div>

// 转换为虚拟 DOM

image.png

插入内容的案例

先看一个案例:当我们点击按钮时会插入一个数字 5 进去:

image.png

在整个过程中,我们可以确定的是 vue 在内部这次更新对于 ul 和 button 是不需要进行更新,需要更新的是 li 的列表。

在 Vue 中,对于相同父元素的子元素节点并不会重新渲染整个列表;因为对于列表中 1、2、3、4 它们都是没有变化的。在操作真实 DOM 的时候,我们只需要在中间插入一个 5 的 li 即可。

那么 Vue 中对于列表的更新究竟是如何操作的呢?

Vue 事实上会对于有 key 和没有 key 会调用两个不同的方法:

  1. 有 key,那么就使用 patchKeyedChildren 方法;
  2. 没有 key,那么久使用 patchUnkeyedChildren 方法;

Vue3 源码对于 Key 的判断

image.png

没有 key 的操作情况下,vue 源码的做法

在没有 key 的情况下,vue3 源码是通过 patchUnkeyedChildren 方法来实现的

具体实现如图下:

第一步:遍历循环通过 patch 方法来做比较
image.png
第二步和第三步:判断新旧 nodes 的长度来进行对节点的删除或者新增
image.png

在有 key 的情况下,vue 源码的做法

有 key 的情况下,vue 对其操作有点复杂,可分为 5 个步骤。

具体如下图所示:

第一步:

image.png

第二步:

image.png

第三步:

image.png

第四步:

image.png

第五步:

image.png

总结

我们可以发现,Vue 在进行 diff 算法的时候,会尽量利用我们的 key 来进行优化操作,所有在没有 key 的情况下我们的效率是非常低的。 在进行插入或者重置排序的时候,保持相同的 key 可以让 diff 算法更加高效。