vue源码解读 - diff算法
导语 最近碰到部分业务场景,代码逻辑需要了解"数组变更后,具体变更了哪一些元素,以及变更的位置.."。于是仔细研究并覆写了一遍针对数组变化的diff算法,在这里做下diff算法的逻辑分享&&源码解读
一.介绍前的准备工作
我们先了解diff方法的运行规则和前提方法.
1.虚拟node进行深度优先 && 同级对比
深度优先:
同级对比
如上面图所示:
每次vnode都是执行同级对比(对应dom同一个父元素)
代码逻辑如下图:
2.简单判断
sameVnode
函数用来进行判断是否是同一个vnode元素。
源代码:
如图所示:
这里有两个重要元素:
key
: 开发者定义的”:key”
sel
: 元素tagName+元素id+元素class
sel的定义源码如下:
vNode构建函数:
3.构建索引
逻辑如图:
二.如何处理元素
尽量不新增/删除dom,如图下所示:
如果是相同vnode,源码如下:
三.开始比较
1.首先会进行时间复杂度 O(n)的while循环,循环条件为 "遍历旧节点数组&&遍历新节点数组,谁先遍历完循环就结束" ,源码如下图:
在每次的循环过程中,会有两大类判断方法:
1-1.首尾比较 && 首尾序号
逻辑:如图上所示,首先在循环遍历前 标记好新,旧节点数组的开始位置和结束位置的序号:oldStartIdx、oldEndIdx、newStartIdx、newEndIdx;其次在循环遍历的过程中采用 "首首比较,尾尾比较,首尾比较";
源码如下:
如果数据为图上所示,那么根据首尾比较方法会有如下图所示结果,最终全部执行了更新操作:
1-2.索引比较 -- 最坏情况,这里的时间复杂度也是O(n),即整个算法复杂度O(n)+O(n)
每次遍历的过程中可能存在"新数组节点新增/旧数组节点删除",那么前后对比就满足不了条件。这里逻辑会进入索引比较;
比如这种情况:
那么,循环中会执行一遍 创建旧数组的索引对象。
那么从创建到比较的整个逻辑图如下:
这里的源码如下:
1-2.1 当旧节点不存在新增的节点时,进行当前oldStartIdx位置的添加:
源码如下:
1-2.2 当旧数组存在节点,那么进行位置移动:
源码:
1.3 当节点遍历完之后:
会存在两种情况,“新数组已经遍历完,但旧数组没有遍历完成” || “旧数组遍历完成,但新数组没有遍历完成”.
故源代码的判断如下:
1.3.1 旧数组没有循环完成:
旧数组没有循环完成的效果如下图所示:
这里注意一个点,我们每次的节点更新会移动序号,即使被删除的节点不在一块 最终也会被 首尾比较算法 "摞在一块" 即 (oldStartIdx~oldEndIdx)。上图所示更加明显一些。
源码在这里就进行批量删除:
1.3.2 新数组没有循环完成:
效果如下图所示:
经过 前后对比&&索引 的过滤后,只会存在 新.末尾节点!==旧节点 及之前的连续的新节点(!==旧节点);
所以这里也被 "摞在一块" ,即 (newStartIdx~newEndIdx)
源码如下:
这样,整个diff的对比算法就已经走完了。所以核心就是:前后对比+索引
四.关键点
关键点大概如下
五.vue3.0对于diff比较前的优化
vue3.0针对"无脑"patchVnode进行了过滤 -- 静态类型Vnode:
老版的源码:
这里,我们再重复下vue2.x系列的对比更新逻辑:
新版的vue3.0增加了 静态类型Vnode,如果是静态类型的vnode 那么直接跳过更新,修改新节点引用即可:
备注:comment类型 目前翻到它的源码也只是更改引用,源码作者加上了一行注释:
这里再多插一句,fragment 碎片类型 为新增的vnode类型, 即:
vue3.0的过滤判断源码如下:
- Python 项目实践三(Web应用程序)第一篇
- Centos 7.0 安装Mono 3.4 和 Jexus 5.6
- 第一个IronPython程序(之二)
- Python 项目实践二(下载数据)第四篇
- Python 项目实践二(下载数据)第三篇
- 体验扁平化的WordPress 后台管理界面
- Python 项目实践二(生成数据)第二篇
- 重新审视SqlDataReader的使用
- Python 项目实践二(生成数据)第一篇
- 删除 WordPress 导航菜单的多余 CSS 选择器
- 删除 WordPress 导航菜单的多余 CSS 选择器
- 使用asp.net 2.0的CreateUserwizard控件如何向自己的数据表中添加数据
- 自定义(修改)WordPress管理后台界面的字体样式
- Sql Server 2005 ROW_NUMBER 函数实现分页
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法