vue2 diff,vue3的diff算法
本文主要介绍vue2的diff算法的相关信息,有助于你更好的理解和使用vue框架。感兴趣的朋友可以了解一下。
前言
双端比较算法是vue2.x采用的diff算法本文只分析双端比较算法的大概流程,具体细节是vue源代码,在这里。
过程
假设当前有两个数组arr1和arr2。
设arr1=[1,2,3,4,5]
设arr2=[4,3,5,1,2]
那么这个过程有五个步骤。
arr1[0]和arr2[0]的比较
arr1[ arr1.length-1]和arr2[ arr2.length-1]的比较
arr1[0]和arr2[ arr2.length-1]之间的比较
arr1[ arr1.length-1]和arr2[0]的比较
比较arr2[0]和arr1的每个元素。
每次比较都从数组的两端开始。如果第一次比较相等,则第一次比较的索引为1。
如果最后比较成功,则比较的结束索引为-1。当开始索引大于结束索引时,比较已经结束。
拆解过程
设arr1=[1,2,3,4,5]
设arr2=[4,3,5,1,2]
设oldStartIdx=0
设olden didx=arr 1 . length-1
设newStartIdx=0
设newEndIdx=arr2.length -1
设oldStartVNode=arr 1[oldStartIdx]
设oldEndVNode=arr1[oldEndIdx]
设newStartVNode=arr 2[newStartIdx]
设newEndVNode=arr2[newEndIdx]
第一轮:
1.1和4不相等。
2.5和2不相等。
3.1和2不相等。
4.5和4不相等。
5.4与旧数组逐一比较,与索引为3的值相等,意味着4的位置从索引3变为0,newStartIdx。
//比较后用u_1表示成功的元素。
[1,2,3,u_1,5] //arr1
[u_1,3,5,1,2] //arr2
第二轮:
1.1和3不相等。
2.5和2不相等。
3.1和2不相等。
4.5和3不相等。
5.3与旧数组逐一比较,等于索引为2的值。3从索引2更改为0,并且newStartIdx
//比较成功后,用u_2表示成功的元素。
[1,2,u 2,u 1,5] //arr1
[u_1,u_2,5,1,2] //arr2
第三轮:
1.1和5不相等。
2.5和2不相等。
3.1和2不相等。
4.5和5相等,5已经从旧数组oldEndIdx位置移动到新的StartIdx位置,新的start idx,旧的end idx-
5.第四步比较成功,进入下一轮。
//比较成功后,用u_3表示成功的元素。
[1,2,u 2,u 1,u 3]//arr 1
[u_1,u_2,u_3,1,2] //arr2
第四轮:
1.1和1相等,1已从旧数组oldStartIdx位置移动到newStartIdx位置,oldStartIdx,newStartIdx。
2.第一步成功了,就可以进入下一轮了。3.第一步成功了,就可以进入下一轮了。
4.第一步成功了,就可以进入下一轮了。5.第一步成功了,就可以进入下一轮了。
//比较成功后,用u_4表示成功的元素。
[u_4,2,u_2,u_1,u_3] //arr1
[u_1,u_2,u_3,u_4,2] //arr2
第五轮:
1.2和2相等,1已经从旧数组oldStartIdx位置移动到newStartIdx位置,oldStartIdx,newStartIdx。
2.第一步比较成功,进入下一轮。
3.第一步比较成功,进入下一轮。
4.第一步比较成功,进入下一轮。
5.第一步相对成功,进入下一轮。
//比较成功后,用u_5表示成功的元素。
[u_4,u_5,u_2,u_1,u_3] //arr1
[u_1,u_2,u_3,u_4,u_5] //arr2
用gif图来表示。
上代码
函数差异(上一个子项,下一个子项){
设oldStartIdx=0 //旧数组起始索引
let end idx=prev children . length-1//旧数组结束索引
let startidx=0//新数组的实际索引
letendidx=next children . length-1//新数组结束索引
设oldStartVNode=prev children[oldStartIdx]
设oldEndVNode=prev children[oldEndIdx]
let newStartVNode=next children[newStartIdx]
设newEndVNode=next children[newEndIdx]
while(old startidx=olden didx newStartIdx=newEndIdx){
如果(!oldStartVNode) {
//当//未定义时向前移动一个位置
oldStartVNode=prev children[oldStartIdx]
} else if(!oldEndVNode) {
//当//未定义时,向后移动一位
oldEndVNode=prev children[-oldEndIdx]
} else if(oldstartvnode . key===newstartvnode . key){//1。开始和开始
oldStartVNode=prev children[oldStartIdx]
newStartVNode=next children[newStartIdx]
} else if(old endvnode . key===newendvnode . key){//2。结束和结束
oldEndVNode=prev children[-oldEndIdx]
newEndVNode=next children[-newEndIdx]
} else if(oldstartvnode . key===newendvnode . key){//3。开始和结束
oldStartVNode=prev children[oldStartIdx]
newEndVNode=next children[-newEndIdx]
} else if(old end vnode . key===new start vnode . key){//4。结束和开始
oldEndVNode=prev children[-oldEndIdx]
newStartVNode=next children[newStartIdx]
}否则{
//5.将新数组的开始元素与旧数组的每个元素进行比较。
const idxInOld=prev children . find index((node)={
if(node node . key===newstartvnode . key){
返回true
}
})
if (idxInOld=0) {
prevChildren[idxInOld]=未定义
}否则{
//newStartVNode是新元素。
}
newStartVNode=next children[newStartIdx]
}
}
}
diff([1,2,3,4,5],[4,3,5,1,2])
我们发现,上述算法完成后,如果新旧数组只改变它们的顺序,它可以完美地区分差异,但如果添加或删除新数组,它就不起作用。所以我们需要在while循环完成后找出添加或删除的元素,那么我们怎么知道哪些是添加的,哪些是删除的呢?
在第五步比较中,将选定的新数组的第一个元素与旧数组的所有元素逐一进行比较。在这里,我们可以发现这个数组是否是新的。如果比较相等,则是位置改变,否则,当前元素是新的。但是while循环的条件是old start idx=old end idx new start idx=newendidx。如果是以下情况,
设arr1=[1,2,3,4,5]
设arr2=[1,2,3,4,5,6,7]
由于循环条件,这将在5 while后结束,所以数组末尾的6和7永远无法走第五步的插入条件。如何判断6和7是新的?让我们看看while循环结束后的索引。
//示例1
设arr1=[1,2,3,4,5]
设arr2=[1,2,3,4,5,6,7]
//它们在//diff之后的索引是
oldStartIdx=5,oldEndIdx=4
newStartIdx=5,newEndIdx=6
//示例2
设arr1=[1,2,3,4,5]
设arr2=[4,5,6,7,1,3,2]
//它们在//diff之后的索引是
oldStartIdx=3,oldEndIdx=2
newStartIdx=6,newEndIdx=5
//示例3
设arr1=[1,2,3,4,5]
设arr2=[7,1,3,5,6,4,2]
//它们在//diff之后的索引是
oldStartIdx=5,oldEndIdx=4
newStartIdx=4,newEndIdx=4
//示例4
设arr1=[1,2,3,4,5]
设arr2=[2,4,1,5,7,3,6]
//它们在//diff之后的索引是
oldStartIdx=3,oldEndIdx=2
newStartIdx=6,newEndIdx=6
我们发现新添加元素的索引与newStartIdx和newEndIdx一一对应。
例1: new startidx小于newEndIdx,为5和6,而arr2中新增元素6的索引为6,arr2中新增元素7的索引为7。此时6和7都已经越过了arr1的长度范围。
例2: new startidx大于newEndIdx,没有对应关系。
例3:新起点IDX等于newEndIdx。我们发现arr2索引为4的元素是新加入的元素6,但它并没有超过arr1的长度范围6倍,而且它恰好是数组中的最后一个元素。
例4: new start idx等于newEndIdx,arr2中索引为6的值是新增加的元素6。
那么结论就是如果while循环结束后newStartIdx小于等于newEndIdx,那么newStartIdx和newEndIdx索引之间对应的元素就是新元素,oldStartIdx总是大于oldEndIdx。
新增完了,删除元素怎么办?看例子。
//示例1
设arr1=[4,3,5,6,7,2,1]
设arr2=[1,3,5,4,2]
//它们在//diff之后的索引是
oldStartIdx=3,oldEndIdx=4
newStartIdx=3,newStartIdx=2
//示例2
设arr1=[7,2,3,5,6,1,4]
设arr2=[5,1,2,3,4]
//它们在//diff之后的索引是
oldStartIdx=0,oldEndIdx=4
newStartIdx=4,newStartIdx=3
//示例3
设arr1=[1,5,4,2,6,7,3]
设arr2=[4,5,1,2,3]
//它们在//diff之后的索引是
oldStartIdx=4,oldEndIdx=5
newStartIdx=4,newStartIdx=3
同理新增的观察套路,发现newStartIdx总是比newStartIdx大,并且需要删除的元素总是在oldStartIdx和oldEndIdx对应的索引之间,那么我们只需要把oldStartIdx和oldEndIdx的元素删除即可,那问题来了,像例子2中oldStartIdx和oldEndIdx索引之间的元素有7,2,3,5,6其中真正需要删除的只有七和6,这样子不就误删了2,3,5么?关键的来了,我们看例子2的2,3,5发现它们走的都是双端比较算法的第五步,第五步写的代码是
const idxInOld=prev children。查找索引((节点)={
如果(节点节点。key===newstartvnode。关键){
返回真实的
}
})
if (idxInOld=0) {
prevChildren[idxInOld]=未定义
}否则{
//newStartVNode是新元素
}
newStartVNode=下一个孩子[newStartIdx]
如果idxInOld0说明在旧数组中找到了,那么我们将学龄前儿童设置为未定义,也就是说2,3,5经过差速器算法后,它们在arr1中的值已经被替换为了未定义,这里也是就为什么在差速器算法开始需要判断!oldStartVNode和!oldEndVnode的原因了,下面我们完善代码
函数差异(上一个子项,下一个子项){
设oldStartIdx=0 //旧数组起始索引
let olden didx=prev children。长度-1//旧数组结束索引
设newStartIdx=0 //新数组其实索引
让newEndIdx=下一个孩子。长度-1//新数组结束索引
设oldStartVNode=prev children[oldStartIdx]
设oldEndVNode=prev children[oldEndIdx]
let newStartVNode=next children[newStartIdx]
设newEndVNode=next children[newEndIdx]
while(old startidx=olden didx newStartIdx=newEndIdx){
如果(!oldStartVNode){//未定义时前移一位
oldStartVNode=prev children[oldStartIdx]
} else if(!oldEndVNode) {
//未定义时后移一位
oldEndVNode=prev children[-oldEndIdx]
} else if(oldstartvnode。key===newstartvnode。key){//1 .开始与开始
oldStartVNode=prev children[oldStartIdx]
newStartVNode=下一个孩子[newStartIdx]
} else if(oldendvnode。key===newendvnode。key){//2 .结束与结束
oldEndVNode=prev children[-oldEndIdx]
newEndVNode=next children[-newEndIdx]
} else if(oldstartvnode。key===newendvnode。key){//3 .开始与结束
oldStartVNode=prev children[oldStartIdx]
newEndVNode=next children[-newEndIdx]
} else if(oldendvnode。key===newstartvnode。key){//4 .结束与开始
oldEndVNode=prev children[-oldEndIdx]
newStartVNode=下一个孩子[newStartIdx]
}否则{
//5.新数组开头元素和旧数组每一个元素对比
const idxInOld=prev children。查找索引((节点)={
如果(节点节点。key===newstartvnode。关键){
返回真实的
}
})
if (idxInOld=0) {
prevChildren[idxInOld]=未定义
}否则{
//newStartVNode是新元素
}
newStartVNode=下一个孩子[newStartIdx]
}
}
if (oldStartIdx oldEndIdx) {
for(;newStartIdx=newEndIdxnewStartIdx){
//新增内容
let vnode=next children[newStartIdx]
}
} else if (newStartIdx newEndIdx) {
对于(设i=oldStartIdxi=oldEndIdx我){ /
/删除内容
}
}
}
diff([1,2,3,4,5],[4,3,5,1,2])
接下来我们使用两个可交换的图像格式图来表示一下差速器过程
1.新增元素
2.减少元素
以上就是详解Vue2的差速器算法的详细内容,更多关于Vue2的差速器算法的资料请关注我们其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。