Vuejs设计与实现 —— 渲染器核心 Diff 算法

前言

当组件发生更新时会重新执行 render 方法生成新的 vnode 节点,而当 新旧 vnode 都是 一组节点 时,为了以最小的性能开销完成 更新操作,需要比较两组子节点,其中用于比较的算法就叫 Diff 算法。Vue 中的 Diff 算法实际上也是一个逐步演进的过程,那么下面就来看看它是如何演进、优化成如今的 Diff 算法的。

简单 diff 算法

在进行 新旧 两组子节点的更新时,去遍历 新旧 一组子节点中 长度较短 的一组,目的是为了尽可能多的调用 pacth 函数进行更新。

理想状态

理想状态 指新旧一组节点中 新旧节点前后位置没有发生变化.

在这个前提下新的一组节点可以比旧的一组子节点多、少或相等:

  • 新旧 一组节点的中 较短 的一组长度,作为公共长度 commonLength
  • 通过以 commonLength 作为循环结束的条件,通过 patch 函数对当前遍历到的 新旧 进行 pacth 更新操作
  • 新旧 一组节点的长度一致,那么意味着其全部是 更新操作
  • commonLength 长度后的,就代表是属于其他多余的节点,这个多余的节点会根据 新旧 的具体情况进行不同的处理:* 新的 一组子节点有剩余,则代表有新的节点需要 挂载,通过 patch 函数进行挂载操作* 旧的 一组子节点有剩余,则代表有旧的节点需要 卸载,通过 unmount 进行卸载操作

非理想状态

非理想状态 指的是 新旧 一组子节点中相 同位置节点不相同.

此时简单 diff 算法仍然会以 commonLength 进行遍历,并通过 patch(n1, n2) 的方式去更新,但在 pacth 函数中由于 n1n2 节点不是相同节点,此时会直接将 旧节点 进行 卸载,然后将 新节点 进行 挂载 操作,哪怕是当前 新旧 一组节点中在不同位置有相同的节点可复用,但简单 diff 算法完全不知道是否有可复用的节点,它完全是依赖于 pacth 来判断当前新旧节点是否是相同的节点。

小结

显然,简单 diff 算法下课通过减少 DOM 操作的次数,提升了一定的更新性能,但在非理想状态下,其更新方式和简单直接的更新方式一致:即卸载旧节点、挂载新节点,这意味着它仍然有被优化的空间。

基于 key 的简单 diff 算法

上述算法的缺陷在于 非理想状态diff 的过程仍然比较固定,即只能比较同位置的节点是否一致,那么优化的方式也是显而易见,只需要引入 key 用来标识 新旧一组子节点中 是否存在相同 key 的节点,若存在则复用 真实 DOM 节点,即更新和移动 DOM 节点即可。

核心

  • 通过遍历 新的一组 子节点中的节点,去 旧的一组 子节点中基于 key 寻找可复用的节点,找到可复用节点进行 patch 更新
  • 根据 lastIndex 决定是否要进行 移动
  • find 变量为 false 时认为当前节点是需要进行 挂载
  • 最后在通过 从旧节点中依次查找新节点中去查找,通过 has 变量判断是否需要进行 卸载

以下是简单的伪代码实现:

function patchChildren(n1, n2, container) {if (typeof n2 === "string") {// 省略代码} else if (Array.isArray(n2.children)) {const oldChildren = n1.children; // 旧的一组子节点const newChildren = n2.children; // 新的一组子节点let lastIndex = 0; // 用于判断当前节点移动的位置// 遍历新的一组子节点:更新、移动、挂载for (let i = 0; i < newChildren.length; i++) {const newVnode = newChildren[i];let find = false; // 标识是否能在旧的一组子节点中找到可复用的节点let j = 0;// 遍历旧的一组子节点for (j; j < oldChildren.length; j++) {const oldVnode = oldChildren[j];// 根据 key 判断是否是相同节点,及是否可复用if (newVnode.key === oldVnode.key) {find = true;// 通过 patch 进行【更新】patch(oldVnode, 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值