了解Vue3是如何进行组件更新的

组件更新

patch

vue 在更新的时候,使用 patch 方法更新新老节点,判断老节点是否可以复用
patch
在 patch 时会先比较新老节点的类型,比较的方式就是判断 type 和 key 是否都相等,类型不同代表不能复用,直接卸载。然后判断新节点的类型,
isSameVNodeType

在这里可以看到有三种类型的节点,使用不同的方法进行处理:

  1. 文本节点
  2. Fragment 节点,vue3 引入 Fragment,在 vue3 的模板中可以使用多个根节点,会被视为 Fragment
  3. element(普通元素)类型、component(组件)类型

component 类型

updateComponent

首先判断新老节点是否发生变化,决定需不需要更新,需要更新就执行
update,就是执行组件自己的更新流程。不需要更新就用老节点就行。通过组件的更新可以看出当父组件更新,子组件没发生变化是不会随着父组件更新的

element 类型

Vue3 通过 patchElement 方法处理 element 类型的节点,首先会去比较新老节点的 props ,如果不同就更新,然后再执行 patchChildren 进行全量比较更新。

patchChildren

在 patchChildren 中对 children 的 vnode 进行 patch,这里存在几种情况:

老节点 新节点 处理方式
-
文本 添加文本节点
数组 添加多个节点
文本 删除多个节点
文本 文本 替换新文本
文本 数组 清空老节点,添加多个新节点
数组 删除多个节点
数组 文本 删除多个节点,添加文本节点
数组 数组 diff

当新老节点都是数组的时候,就会进入到 diff 的流程。

存在 key

当节点有 key 时,就会进入到 diff 流程,Vue3 通过 patchKeyedChildren 进行处理。

首先声明了一些变量

1
2
3
4
let i = 0              /* 记录索引 */
const l2 = c2.length /* 新vnode的数量 */
let e1 = c1.length - 1 /* 老vnode 最后一个节点的索引 */
let e2 = l2 - 1 /* 新节点最后一个节点的索引 */

从头对比


从头对比就是用 isSameVNodeType 检查新老节点是否能一一相等,如果相等就 patch,如果发现不等就 break 跳出头部对比流程。

从尾对比


从尾对比就同样用 isSameVNodeType 从尾开始遍历检查新老节点是否相等,直到遇到不等的就 break 跳出从尾对比流程

比较节点数

进行完头尾对比后会出现三种可能

  1. 老节点全部 patch ,新节点有剩余,说明有新增节点,就需要添加这个节点
  2. 新节点全部 patch ,老节点有剩余,说明有节点被删除,老节点剩余的节点不需要了,需要全部删除
  3. 新老节点全部有剩余,说明节点可能发生了移动,新增,删除

剩余的节点

keyToNewIndexMap


keyToNewIndexMap 是一个 [新节点的key, 索引] 的 map 集合,方便通过 key 来找到老节点的索引。

遍历老节点

这一步的主要过程就是通过老节点的 key 去查找索引,如果没有 key,就遍历剩下的新节点,看有没有和该节点相等的,如果有,就证明有可复用的老节点,直接进行 patch,如果找不到索引,就说明不可复用,直接删除,最后把新老节点对应的索引保存在 newIndexToOldIndexMap,结构是 [新节点索引, 老节点索引]

1
2
const newIndexToOldIndexMap = new Array(toBePatched)
for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0

newIndexToOldIndexMap 是长度和新节点长度相等的,初始值都是 0 的数组。

经历过上面的步骤,老节点都已经被 patch 过了,剩下的就是新节点有新增、移动两种情况,在 newIndexToOldIndexMap 中没有找到新节点对应的值,说明是新增的节点

最长递增子序列

求出最长递增子序列,根据这个作为基准移动节点,使用最长递增子序列作为基准可以使节点移动最少。

最长递增子序列就是一个序列中,找到一个最长的子序列,并且子序列中的元素是按照递增顺序排列的。

不存在 key

没有 key 的时候会进入 patchUnKeyedChildren 来处理

首先比较老节点和新节点的 length ,获取其中较小的值,然后遍历进行 patch
然后如果老节点长度大于新节点,进行删除
如果老节点长度小于新节点,进行新增


了解Vue3是如何进行组件更新的
https://l1ushun.github.io/2024/05/17/vue3-diff/
作者
liu shun
发布于
2024年5月17日