最近碰到部分业务场景,代码逻辑须要了解"数组变动后,具体变动了哪一些元素,以及变动的位置..."。因而仔细研究并覆写了一遍针对数组变化的diff算法,在这里作下diff算法的逻辑分享&&源码解读vue
咱们先了解diff方法的运行规则和前提方法.node
1.虚拟node进行深度优先 && 同级对比算法
深度优先: 同级对比
如上面图所示:数组
每次vnode都是执行同级对比(对应dom同一个父元素)微信
代码逻辑以下图: 2.简单判断markdown
sameVnode函数用来进行判断是不是同一个vnode元素。dom
源代码: 如图所示:
这里有两个重要元素:函数
key : 开发者定义的”:key”优化
sel: 元素tagName+元素id+元素classspa
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针对"无脑"patchVnode进行了过滤 -- 静态类型Vnode:
老版的源码: 这里,咱们再重复下vue2.x系列的对比更新逻辑:
新版的vue3.0增长了 静态类型Vnode,若是是静态类型的vnode 那么直接跳过更新,修改新节点引用便可:
备注:comment类型 目前翻到它的源码也只是更改引用,源码做者加上了一行注释:
这里再多插一句,fragment 碎片类型 为新增的vnode类型, 即:
vue3.0的过滤判断源码以下:
更多精彩内容,尽请关注腾讯VTeam技术团队微信公众号和视频号
原做者:陈碧松
未经赞成,禁止转载!