vue2的diff算法(源码位置:src/core/vdom/patch.js)
父元素同层节点判断 path()
- 判断是否为首次渲染,若是,则直接创建dom
- 若新Vnode不存在,且Oldnode存在,则销毁老的node
- 仅同层进行比较,标签类型及key值,若不一致,则进行替换,若完全一致,则执行patchVnode()进行节点的比较
子节点的比较 pathVnode()
- 将oldVnode的dom赋值到vnode节点上,遍历更新oldVnode上的所有属性
- 若vnode不是文本节点,判断: (1)若新老子节点都存在,且不相同,则updateChildren()进行比较 (2)若仅新子节点存在,老节点无子节点,若老节点为文本节点,则清空老节点后,新增新节点 (3)若仅旧子节点存在,则销毁旧节点 (4)若新老都不存在子节点,则判断旧节点是否为文本节点,若是则清空
- 若新节点是文本节点,判断: (1)是否与老节点文本相同,若不同,将新节点真实化后添加进dom中
子节点的比较更新 updateChildren() ----重点
- 四指针:oldStartIndex,oldEndIndex,newStartIndex,newEndIndex 【分别代表着,新或旧列表节点的头尾】
- 比较规则:新头-旧头;新尾-旧尾;旧头-新尾;旧尾-新头;
- 若匹配上: 1.头和头匹配上: 打补丁 oldStartIndex++; newOldStatIndex++; 2.旧尾和新尾匹配: 打补丁 oldEndIndex--; newEndIndex--; 3.旧头和新尾匹配: * 打补丁 * 将旧列表的第一个节点,移动到oldEndVnode之后(inSertBefore(oldStartVnode,oldEndVnode.nextSibling)) oldStartIndex++; newEndIndex--; 4.旧尾和新头匹配: * 打补丁 * insertBefore(oldEndVnode,oldStartVnode) oldEndIndex--; newStartIndex++;
- 若四次都没匹配上: 1.拿新列表的第一个节点,在就列表中找匹配: * 找到: (1) 先insertBefore(oldFindVnode,oldStartVnode),并将原来的地方置为undefined(比较的时候跳过) (2) newStartIndex++
编辑切换为居中
添加图片注释,不超过 140 字(可选)
-
未找到: 直接添加 newStartVnode 到最前面 * 若新旧列表节点的个数不一样(节点的添加、删除): 若 oldStartIdx > oldEndIdx,将新列表中剩余的节点添加 若 newStartIdx > newEndIdx,将旧节点中剩余节点移除
实例步骤:
第一次,四个对比都没匹配上
第二次,旧尾与新头匹配
第三次,同第二次
第四次,旧首与新首匹配
注意:第四次,oldStartIndex++到B,此时B节点已经是undefined(第一次移动时,置为的undefined),undefined节点不会参与比较,进入下一循环
第五次,首先匹配旧首-新首,此时oldStartIndex>oldEndIndex,则会跳出循环
跳出循环之后,进行相关判断,对节点进行添加,或者删除
# vue3的diff
执行步骤:
- 前置,后置比较方式,将不需要移动的节点排除(即不需要同vue2,进行一个dom与另一个dom整体比较)
- 最长递增子序列
画图来分析: 1. 使用前置/后置法,将不需要改变的节点排除
解释:
- 初始定义三个变量j,preEndIndex,newEndIndex;
- 从两边向中间靠拢,若preNode等于newNode,移动j或preEndIndex或newEndIndex对应的值;
- 先对比左边,循环判断j的移动,若preNode!==newNod则跳出循环;
- 再进行右边的对比,循环判断,若preNode===newNod,则preEndIndex--且newEndIndex--;反之,则退出循环
- 每次循环时,都判断一下边界,若触发边界(j> preEndIndex || j>newEndIndex),则退出前置、后置操作
2.前置后置方法后,对边界情况进行判断,做对应的插入或者删除操作
解释:存在两种情况
- j > prevEndIndex && j <= newEndIndex(新的列表字节比旧的列表多),则将j到newEndIndex对应位置的节点插入;
- j > newEndIndex && j <= prevEndIndex(旧列表还存在节点,而新列表已对比完成),prevEndIndex对应所有节点进行删除;
3.若没有达到边界情况,则将新旧列表中的剩余节点进行真正的比较(即diff算法),进行最长递增子序列的查找
解释:
- 将新列表中,剩余节点数创建为对应长度的数组source,并填充-1值
- 遍历旧列表剩余节点,找到新列表剩余节点对应的下标,并存到source当中
- 查找过程中,若旧的列表节点,在新列表中不存在或者所有新列表中的节点都已找到匹配(剩余新列表的个数小于剩余旧列表的个数),则删除对应旧列表中的节点
- 最后source的结果就是,每个新节点在旧列表中对应的位置,其中-1代表需要新添加的节点
- 判断是否需要移动,若source内容都是递增的,则不需要移动,反之需要;
- 若需要移动
- 查找source中最长递增子序列,所对应的下标,结果为lis:[0,1]
- 从后向前遍历source数组,比较source[i]的内容:
- 若source[i]===-1,则添加节点
- 若source[i]等于最长递增子序列对应的值,则不需要移动
- 若非前两者的情况,则需要移动,将节点插入到对应的位置
- 若不需要移动
- 只需要判断一下source当中是否存在有-1的值,若存在则进行节点插入