vue3 - diff算法之快速diff算法

本文介绍了Vue3中快速Diff算法的预处理步骤,包括处理前置和后置节点、新增节点和删除节点的情况。通过预处理减少不必要的DOM操作,提高性能。文章详细阐述了预处理过程、新增和删除节点的判断,以及如何移动元素,帮助理解Vue3的更新机制。
摘要由CSDN通过智能技术生成

快速Diff算法

预处理

前面讲到简单Diff算法和双端Diff算法,它们使用不一样的对比规则对虚拟节点的 type(元素名)和 虚拟节点的key(唯一标识)来区分是否有可以复用的旧节点。快速Diff算法也是一样的,不过要比简单Diff和双端Diff多了一步预处理的操作。

什么是预处理

什么是预处理呢?用文本来举个例子:

const TEXT1 = 'I am a front-end developer'
const TEXT2 = 'I am a back-end developer' 

要对这两段文本进行diff,首先会对它进行全等比较:if (TEXT1 === TEXT2) return,如果全等旧没有必要进入核心的diff步骤了。除了全等比较,还会对他们进行前缀于后缀的比较。一眼就能看到这两段文本的头部和尾部分别有一段相同的内容。

I am a front -end developer

I am a back -end developer

对于相同的内容,不需要进行Diff操作,因此对于 TEXT1 和 TEXT2 来说,真正需要Diff操作的部分是:

TEXT1:front
TEXT2:back 

这是一种简化问题的方式,好处是可以在特定情况下能够轻松判断文本的插入和删除。

预处理便是将上面的例子掐头去尾的过程,那么再看另外一个例子:

const TEXT3 = I like you
const TEXT4 = I like you too 

这两段文本经过预处理之后可以得到:

TEXT3:
TEXT4:too 

如果 TEXT3 是新的内容,那么只需要删除多余的 too 就可以完成文本更新;否则,添加 too 完成文本更新。

预处理要怎么做 - 例一

快速Diff算法就是借鉴了纯文本的diff算法中预处理的步骤。以下面的两组节点为例:

const oldChildren = [{ type: 'p', key: '1' },{ type: 'p', key: '2' },{ type: 'p', key: '3' }
]
const newChildren = [{ type: 'p', key: '1' },{ type: 'p', key: '4' },{ type: 'p', key: '2' },{ type: 'p', key: '3' }
] 



从图中可以看到,两组节点具有相同的前置节点 p - 1,以及相同的后置节点 p - 2、p - 3。对于相同的前置节点和后置节点,由于它们在新旧两组子节点中的相对位置不变,所以不需要移动它们,但仍然要在它们之间打补丁。

处理前置节点

对于前置节点,可以建立索引 j ,初始值为0,指向两组子节点的开头。开启一个while循环,让索引 j 递增,直至遇到不同的节点为止。

function patchKeyedChildren (n1, n2, container) {const newChildren = n1.childrenconst oldChildren = n2.children// 处理相同的前置节点// 索引 j 指向新旧两组子节点的开头let j = 0let oldVNode = oldChildren[j]let newVNode = newChildren[j]// while 循环向后遍历,直到遇到不同 key 值的节点为止while (oldVNode.key === newVNode.key) {// 调用 patch 函数进行更新patch(oldVNode, newVNode, container)// 让索引 j 递增以对下一个节点进行处理j++oldVNode = oldChildren[j]newVNode = newChildren[j]}
} 

上面使用while循环查找所有相同的前置节点,并调用patch函数进行打补丁,直到遇到key值不同的节点为止。这样就完成了对前置节点的预处理。



处理后置节点

接下来就要处理后置节点,因为新旧两组子节点的数量不同所以还需要两个索引,指向新旧两组子节点的最后一个节点。然后再开启一个while循环从后向前遍历这两组子节点,直到遇到key值不同的节点为止。

function patchKeyedChildren (n1, n2, container) {const newChildren = n1.childrenconst oldChildren = n2.children// 处理相同的前置节点// 索引 j 指向新旧两组子节点的开头let j = 0let oldVNode = oldChildren[j]let newVNode = newChildren[j]// while 循环向后遍历,直到遇到不同 key 值的节点为止while (oldVNode.key === newVNode.key) {// 调用 patch 函数进行更新patch(oldVNode, newVNode, container)// 让索引 j 递增以对下一个节点进行处理j++oldVNode = oldChildren[j]newVNode = newChildren[j]}// 获取最后的子节点的索引值let oldEnd = oldChildren.length - 1let newEnd = newChildren.length - 1// 获取最后的子节点oldVNode = oldChildren[oldEnd]newVNode = newChildren[newEnd]// while 循环从后向前遍历,直至遇到不同 key 值得节点为止while (oldVNode.key === newVNode.key) {// 调用 patch 函数进行更新patch(oldVNode, newVNode, container)// 让索引 j 递减以对下一个节点进行处理(因为是从后往前所以递减)oldEnd--newEnd--oldVNode = oldChildren[oldEnd]newVNode = newChildren[newEnd]}
} 

与处理相同得前置节点一样,在while循环内,需要调用patch函数进行打补丁,然后递减两个索引oldEnd、newEnd。



新增节点

从图中可以看到,相同的前置节点和后置节点被处理完之后,旧的一组一节点全部被处理了,而在新的一组子节点中,还有一个没有被处理的节点 p - 4。因此得出,p - 4 是一个新增节点:

oldEnd < j 成立,说明在预处理时,所有旧子节点都处理完毕了

newEnd >= j成立,说明预处理后,新的一组子节点中,存在未被处理的节点,这些节点就是新增的节点



索引值在 j 和 newEnd 之间的任何节点都需要作为新的子节点进行挂载,挂载新元素就要找到正确的锚点元素。从上图中看到,新增节点应该挂载到节点 p - 2 所对应的真实DOM前面,所以将 p - 2 作为挂载操作的锚点元素。

function patchKeyedChildren (n1, n2, c
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue2和Vue3都是流行的前端框架,它们在虚拟DOM的diff算法上有一些区别。下面我会详细介绍一下Vue2和Vue3的diff算法Vue2的diff算法Vue2使用的是经典的双指针算法来进行虚拟DOM的diff过程。大致的步骤如下: 1. 创建新旧虚拟DOM树(VNode),并进行比较。 2. 对新旧虚拟DOM树进行同层级的节点对比,找出差异。 3. 如果两个节点类型不同,则直接替换整个节点及其子节点。 4. 如果两个节点类型相同,则进行更详细的比较。 5. 对于有key的节点,通过key来匹配新旧节点,减少移动节点的操作。 6. 对于没有key的节点,使用遍历的方式进行比较,效率较低。 7. 如果在旧节点集合中找不到匹配的节点,则认为是新增节点,创建并插入到正确的位置。 8. 如果在新节点集合中找不到匹配的节点,则认为是删除节点,从DOM中移除。 Vue2的diff算法存在一些缺点: 1. 每次更新都需要对整个VNode树进行遍历,效率较低。 2. 对于没有key的节点,会使用遍历的方式进行比较,导致性能下降。 3. 当VNode树较大时,diff算法的性能会受到影响。 Vue3的diff算法Vue3采用了一种更高效的diff算法,称为静态标记和提升(Static Markup and Hoisting)。它的主要思想是通过编译阶段的静态分析,将动态节点和静态节点进行标记,从而减少diff的过程。 Vue3的diff算法具体步骤如下: 1. 在编译阶段,通过静态分析将模板中的动态节点和静态节点进行标记。 2. 对于静态节点,会将其提升为常量,并在patch过程中跳过对这些节点的比较。 3. 对于动态节点,会使用类似Vue2的diff算法进行比较和更新。 4. 对于列表渲染(v-for)的情况,会通过唯一的key来进行精确匹配和复用节点。 5. 通过静态标记和提升,减少了不必要的比较和更新操作,提高了diff算法的效率。 Vue3的diff算法相比Vue2有以下优点: 1. 在编译阶段进行静态标记和提升,减少了运行时的工作量。 2. 可以更精确地识别出动态节点和静态节点,减少不必要的比较和更新操作。 3. 对于列表渲染,通过唯一的key进行精确匹配和复用节点,提高了性能。 总结: Vue2和Vue3的diff算法都是基于虚拟DOM的思想,但Vue3引入了静态标记和提升的概念,通过编译阶段的静态分析来优化diff过程,提高了性能。在实际开发中,如果需要更高的性能,推荐使用Vue3。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值