目录
1:没key
补充1:为什么不建议用index作为key值?
2:有key
2.1:vue2diff
2.2:vue3diff
补充2:vue3的最长递增子序列
补充3:vue2diff和vue3diff的比较
补充4:虚拟dom
补充5:vue3diff源码解析(不是我写的,我感觉别个写的很好,直接看)
暂时先写这么多,如有错误,欢迎指正!
1:没key
没有key值,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。
例如:现在有abcd四个节点,需要在bc节点间添加一个e节点,没有key值时,会将c节点变成e节点,d节点变成c节点,最后加入d节点(如下图一)。有key值时,直接在bc之间加入e节点(如下图二)。key值的作用,简单来说就是给节点加一个唯一标识,让diff算法可以识别这个节点,只更新对应的dom而不是全部更新。
补充1:为什么不建议用index作为key值?
因为index不稳定,比如数组中间添加一个值,它后面的元素index值就全部变了,后面dom需全部更新,性能并没有优化。
2:有key
2.1:vue2diff
简单来说,就是先老节点与新节点,头对头->尾对尾->头对尾->尾对头,再不行就从根据新节点key值,找对应的老节点,找到更新对应的真实dom.以上五步是不符合条件依次往下,符合条件,新老节点的startIndex或endIndex会向中间靠拢,进行下一节点的对比。
以下分五种情况讲解:
- “理想状态”,没变(如下图三):依次复用dom,oldStart和newStart向后移动,直至完成。
- “头对不上头”,(如下图四):头跟头比,对不上,尾巴对上了,从尾巴开始复用dom,oldEnd和newEnd向前移动,直至完成,多的删掉。
- “头对不上头,尾也对不上尾”,(如下图五):头跟头没对上,尾跟尾也没对上,接下来就开始交叉对比了,首先是老的节点头跟新的节点尾对比,对上了就更新dom,对应的新旧节点头尾指针变化。(这里用红色表示对不上,没有更新dom ,蓝绿色表示对上了,有更新dom)
- “头对不上尾”,(如下图六):除了头对不上头,尾对不上尾,头也对不上尾了,那就尾对头,对上了就渲染dom。(这里第一个头尾是老节点的,第二个头尾是新节点的)
- “完全乱序”,(如下图七):头对不上头,尾对不上尾,头尾交叉也对不上,那就根据key值来找,newStart的key,按顺序从老节点中找到key值对应的节点,从oldStart开始哈,找到后渲染出来,就继续头对头,,,比下去。
以上就是vue2diff的完整解析了,这里不贴源码了~
2.2:vue3diff
以下分两种情况简单讲解:
- “头对上头,或者尾对上尾:如vue2diff的前两种情况,这里不做过多讲解。
- ”头对不上头,尾也对不上尾“,如图八系列):这里老节点与新节点依次对比,找到了打个位置标记,当全部老节点判断完成后,借助最长递增子序列和前面的位置标记(书面语新旧节点位置映射表),再进行dom节点的复用与渲染。
- 先确定头对不上头,尾对不上尾,以上是无序多个节点
- 列出新旧节点映射表,这里是为了区分是否新增节点,默认是0,这一步同时删掉需要删除的节点。
- 找出最长递增子序列,这里是为了做最少的改动。
- 渲染。如上图例子,我们从newEnd开始看,新旧节点映射表如图,可以知道f是新增节点,直接渲染。根据bdac来找最长递增子序列,是ac,ac位置不变,db位置移动。
补充2:vue3的最长递增子序列
vue3寻找最长递增子序列同时用了二分查找和贪心算法,这里不详细讲了。
补充3:vue2diff和vue3diff的比较
1:vue2diff是基于递归的双指针,vue3diff是基于数组的动态规划。
2:vue2全部比较,vue3不比较静态节点。
3:vue2的diff完全乱序的列表需要借助key,性能会好一些,vue3是借助位置变化值。(注意:有听到一个说法,vue3diff不需要key值,单看核心diff好像是哦,但是假设应对完全乱序的列表,没有key的话,vue3的做法和vue2是一样的吧~参考1)
补充4:虚拟dom
虚拟 DOM (Virtual DOM,简称 VDOM) 是一种编程概念,意为将目标所需的 UI 通过数据结构“虚拟”地表示出来,保存在内存中,然后将真实的 DOM 与之保持同步。官方文档给的例子如下:
const vnode = {
type: 'div',
key:null,
props: {
id: 'hello'
},
children: [
/* 更多 vnode */
]
}
<div id="hello">
//...
</div>
补充5:vue3diff源码解析(不是我写的,我感觉别个写的很好,直接看)
vue3diff源码解析