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

图片

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

图片

diff方法的运行规则和前提方法

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

深度优先:

图片

同级对比:

图片

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

代码逻辑如下图:

图片

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

图片

如图所示:

图片

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

sel的定义源码如下:

图片

vNode构建函数:

图片

第三是构建索引。

图片

逻辑如图:

图片

图片

如何处理元素

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

图片

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

图片

图片

开始比较

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

图片

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

1)首尾比较&首尾序号

图片

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

源码如下:

图片

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

图片

2)索引比较

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

图片

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

图片

这里的源码如下:

图片

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

    图片

源码如下:

图片

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

    图片

源码:

图片

3)当节点遍历完之后

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

图片

  • 旧数组没有循环完成

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

图片

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

图片

  • 新数组没有循环完成

效果如下图所示:

图片

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

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

图片

图片

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

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

图片

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

图片

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

图片

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

图片

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

图片

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

图片

图片

数组比较的应用

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

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

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

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

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

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

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


腾讯云技术社区
最专业的云解读社区
21.6k 声望
17.3k 粉丝
0 条评论
推荐阅读
从码力到算力的“狂飙”,探元宇宙的未来风向
导语 | 随着 GPT-4 的问世,大大加速催化了 AIGC 与元宇宙的融合发展,在大家对元宇宙的了解逐渐深入之后,越来越多企业不断探索元宇宙的发展机遇。那么在技术驱动的数字时代,我们该如何看待元宇宙的未来发展?...

腾讯云开发者

封面图
从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木75阅读 7k评论 16

从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木45阅读 8.5k评论 6

从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...

乌柏木35阅读 6.6k评论 10

从零搭建 Node.js 企业级 Web 服务器(五):数据库访问
回顾 从零搭建 Node.js 企业级 Web 服务器(一):接口与分层,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,控制层与服务层实现了业务处理过程,模型层定义了业务实体并以 对象-关系...

乌柏木34阅读 5k评论 9

一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。
前言秒杀和高并发是面试的高频考点,也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目,提交PR,提高竞争力。早日上岸,升职加薪。知识点详解秒杀系统架构图秒杀流程图秒杀系统设计这篇文章一万多...

王中阳Go32阅读 2.4k评论 1

封面图
从零搭建 Node.js 企业级 Web 服务器(十三):断点调试与性能分析
Node.js 官方提供了断点调试机制,出于安全性考虑默认为关闭状态,可以通过 node 参数 --inspect 或 --inspect-brk 开启,配合 IDE 能够非常方便地调试代码,本章就上一章已完成的项目 licg9999/nodejs-server-ex...

乌柏木31阅读 4.1k评论 9

21.6k 声望
17.3k 粉丝
宣传栏