vue2 diff,vue3的diff算法

  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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: