前言
这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情” vue2、vue3也好,react也罢,diff算法在它们里面都充当着十分重要的作用,diff算法是vue3最难的地方,diff处于在运行时的代码里面,他充当极其重要的作用,那么我们今天就来研究vue3里面的diff算法。
正文
diff算法处于运行时中哪个地方
翻阅源码,会发现diff算法处在vue3的runtime-core的renderer.ts中,在前面我们分析过,vue3运行时有两个核心处理步骤,其中一个用processElement去处理element类型,另外一个是processComponent去处理组件类型,其中processElement是去处理Element类型,在Element的最后步骤,就是对应的diff算法,是在patchKeyedChildren中实现的,diff算法主要用于对比两个数组节点,然后做增删改查操作,这样有利于提升效率。
四个指针
搞清diff算法需要弄清四个指针,源码中定义如下,其中i代表遍历的index,l2代表新数组的长度,e1代表旧得数组节点目前对应的index,e2则对应新元素对应的index。后面的实现都围绕这四个指针
let i = 0
const l2 = c2.length
let e1 = c1.length - 1 // prev ending index
let e2 = l2 - 1 // next ending index
diff算法分为哪几种
diff算法大致分为五种,翻阅vue3源码会发现,人家已经注释好了。
1.sync from start(从左面开始遍历)
源码例子如下:
// (a b) c
// (a b) d e
这种情况下,相同的节点可以复用,不是的话直接追加。遍历的时候e1、e2不用变化。
2.sync from end(从右侧开始遍历)
源码例子如下,这种情况下,e1 e2的长度会进行--的操作。
// a (b c)
// d e (b c)
3.sync from end(左侧多了节点或者右侧多了节点)
// 3. common sequence + mount
// (a b)
// (a b) c
// i = 2, e1 = 1, e2 = 2
// (a b)
// c (a b)
// i = 0, e1 = -1, e2 = 0
这种情况直接直接追加节点就好,这种情况下需要注意的是需要设置锚点,就是在没有对应的旧节点的话,需要传入一个它之后的节点作为锚点,防止节点混乱。
4.sync from end(左侧多了节点或者右侧少了节点)
// 4. common sequence + unmount
// (a b) c
// (a b)
// i = 2, e1 = 2, e2 = 1
// a (b c)
// (b c)
// i = 0, e1 = 0, e2 = -1
这种情况直接unmount就行了。
5.unknown sequence
// 5. unknown sequence
// [i ... e1 + 1]: a b [c d e] f g
// [i ... e2 + 1]: a b [e d c h] f g
// i = 2, e1 = 4, e2 = 5
这种情况下,需要找到最大递增子序列,从而实现最大复用,vue3自己实现了一套算法,与此同时,vue3在这里面用map去存储对应的key,这个时候就要说到key的作用了,从diff算法角度来看,它可以提高diff效率,从而实现O(1)的时间查找效率
结尾
到这里,diff算法就讲完了,个人觉得讲的还比较通俗易懂。