由浅入深读透vue源码:diff算法

42b7486c81f63fcf0cbedae3c90aa1be.gif导语 | 开发者工作中,研究代码逻辑常需要思考这个问题:数组变更后,具体变更了哪一些元素?变更的位置如何?本文作者陈碧松解析并覆写了针对数组变化的diff算法逻辑。希望本文对你有帮助。

bb6dd62fbd9dda7d72c9c97ac456aff6.jpegdiff方法的运行规则和前提方法

为了了解diff方法的运行规则和前提方法,首先我们通过几个图快速区别虚拟node进行深度优先和同级对比。

深度优先:

e46dae02db00e006f8dc337e087e9cb7.png

同级对比:

d63d9cf89d791ef1349cc4a37675736a.png

如上面图所示,每次vnode都是执行同级对比。(对应dom同一个父元素)

代码逻辑如下图:

13b21d4079edf340b352cfd368aa7119.png

第二,简单判断:`sameVnode`函数用来进行判断是否是同一个vnode元素。源代码如下:

0c798ef1e3b8a85da2b6f8c931a7fd17.png

如图所示:

fc779e29d2128d16f45bd8a2b56e992d.png

这里有两个重要元素:`key` : 开发者定义的”:key”;`sel `:  元素tagName+元素id+元素class。

sel的定义源码如下:

fa542ad883f9087e83040920b5e43459.png

vNode构建函数:

36e24f519e5de7876ac9fac88af83348.png

第三是构建索引。

e705115d5eb4cbc257c5ab0612ed0c1c.png

逻辑如图:

1a601f9de8805856c02e181a7de587d0.png

b39c57ba3262eca35f07d1de0f11a392.jpeg如何处理元素

尽量不新增/删除dom。如图下所示:

6bb810f80b8cc728b257cb38e51e4826.png

如果是相同vnode,源码如下:

c71a288eeb7facc1342d3b2b33924bd8.png

c35c94a88220ff454b327f4a0cc58def.jpeg开始比较

首先会进行时间复杂度O(n)的while循环,循环条件为“遍历旧节点数组&&遍历新节点数组,谁先遍历完循环就结束”。源码如下图:

0e3ac14f0c830e547a007be836e9403d.png

在每次的循环过程中,会有两大类判断方法:

1)首尾比较&首尾序号

ff06688b3617c33b30591d480e753d7c.png

逻辑:如图上所示。首先在循环遍历前标记好新,旧节点数组的开始位置和结束位置的序号:oldStartIdx、oldEndIdx、newStartIdx、newEndIdx;其次在循环遍历的过程中采用首首比较,尾尾比较,首尾比较"

源码如下:

564e04aa641138d2a856b4e6ba4c9bbc.png

如果数据为图上所示,那么根据首尾比较方法会有如下图所示结果,最终全部执行了更新操作:

177dd716afa33505782ad622e50792e4.png

2)索引比较

最坏情况,这里的时间复杂度也是O(n),即整个算法复杂度O(n)+O(n)。每次遍历的过程中可能存在"新数组节点新增/旧数组节点删除",那么前后对比就满足不了条件。这里逻辑会进入索引比较:比如这种情况:

a1ccd77f5537493b7581392ae487aba6.png

那么循环中会执行一遍,创建旧数组的索引对象。从创建到比较的整个逻辑图如下:

027d143d5e37865a5166ba0e19420390.png

这里的源码如下:

76a4ea392f53c22fd5d8ae968f6856a8.png

  • 当旧节点不存在新增的节点时,进行当前oldStartIdx位置的添加

    2289c3dc3640e4273a388c26bf1d7e07.png

源码如下:

24f2c9454351fc728bfbd67d89c92447.png

  • 当旧数组存在节点,那么进行位置移动

    994158a68f3f7b633b7cc764909420be.png

源码:

3f841124207d9478e5d484e72b251a1b.png

3)当节点遍历完之后

会存在两种情况:新数组已经遍历完,但旧数组没有遍历完成;旧数组遍历完成,但新数组没有遍历完成。故源代码的判断如下:

d4b200d0cbe3962c4b8e251f17f28805.png

  • 旧数组没有循环完成

旧数组没有循环完成的效果如下图所示:

9ae477e9ffebb685aa834d6639f7bf5f.png

这里注意一个点,我们每次的节点更新会移动序号,即使被删除的节点不在一块最终也会被首尾比较算法“摞在一块”(oldStartIdx~oldEndIdx)。上图所示更加明显。源码在这里就进行批量删除:

0a89b91736960476363dd440c3721618.png

  • 新数组没有循环完成

效果如下图所示:

9189e41d2f63471954d4426850899d1f.png

整体来说,有几个关键点:简单对比;创建旧数组的索引表;首位对比&首尾索引&vnode位置移动;索引添加/位移;剩余部分批量处理添加/删除

经过前后对比&索引的过滤后,只会存在新.末尾节点!==旧节点及之前的连续的新节点(!==旧节点),所以这里也被“摞在一块”,即 (newStartIdx~newEndIdx)。源码如下。这样,整个diff的对比算法就已经走完了。核心就是:前后对比+索引。

16eab893e386e5d390ff6b3645fa17a8.png

5995b87e1ee0e6a374833dfa49049df8.jpeg

vue3.0对于diff比较前的优化

vue3.0针对“无脑”patchVnode进行了过滤--静态类型Vnode老版的源码:

0b6678cda82b72d65ea2e7fe4be4e260.png

这里,我们再重复下vue2.x系列的对比更新逻辑:

db18ee9e51ff8d98238f4ccc016bc628.png

新版的vue3.0增加了静态类型Vnode。如果是静态类型的vnode,直接跳过更新,修改新节点引用即可。

8ce568f316e6f4678d0596f88887a963.png

comment类型目前翻到它的源码也只是更改引用,源码作者加上了一行注释。

85fa7716ffeacf65882c37594fb7bf50.png

补充一下,flagment碎片类型为新增的vnode类型,即:

cbaa8e16164c13096904ffef18817a58.png

vue3.0的过滤判断源码如下:

e49d80cd31f7d3f56d3f132a8e032e0e.png

db359b90d749b772bc698461bb26cfc4.jpeg

数组比较的应用

由于我们想监听数组的变化,参考了diff算法覆写类似的逻辑,用来在update,add,dels时,代码层面获取操作的具体节点明细(新旧节点的位置,内容)。希望本文对你有帮助。

你可能感兴趣的腾讯工程师作品

804869891be5481263803dedfb75bf67.png

736634e7b27b63d82966c9e553e43f47.png

| 优雅应对故障:QQ音乐怎么做高可用架构体系?

| 最全Go select底层原理,一文学透高频用法

| 十亿人都在用的健康码,运维体系是怎么设计的

详解全网最快Go泛型跳表【内附源码】

技术盲盒:前端后端AI与算法运维工程师文化

9c4784cdecd29db38d15411742608c8c.png

🔹关注我并点亮星标🔹

工作日晚8点 看腾讯技术、学专家经验

点赞|分享|在看 传递好技术

215945b24eb3219f6aeb479667b92ce8.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值