从vue源码角度分析key的作用

249 阅读3分钟

在文档中说到:

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

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

需要了解的俩个概念

Vnode

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

以代码为例:

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

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

const vNode = {
  type: 'div',
  props: {
    class: 'text',
    style: {
      'font-size': '26px',
      color: 'pink'
    },
  },
  children:'这是一段文本内容'
};

虚拟DOM

文档中说到:

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

以代码为例:

<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算法更加高效。