diff算法介绍( 二 )


移动:组件D已经在集合(A,B,C,D)里了 , 且集合更新时 , D没有发生更新 , 只是位置改变 , 如新集合(A,D,B,C) , D在第二个 , 无须像传统diff , 让旧集合的第二个B和新集合的第二个D 比较 , 并且删除第二个位置的B , 再在第二个位置插入D , 而是 (对同一层级的同组子节点) 添加唯一key进行区分 , 移动即可 。
移动的条件:

  • 对新集合中的节点进行循环遍历 , 新旧集合中是否存在相同节点
  • nextIndex: 新集合中当前节点的位置
  • lastIndex: 访问过的节点在旧集合中最右的位置(最大位置)
  • If (child._mountIndex < lastIndex)
情形一:新旧集合中存在相同节点但位置不同时 , 如何移动节点
diff算法介绍

文章插图
 
(1)看着上图的 B , React先从新中取得B , 然后判断旧中是否存在相同节点B , 当发现存在节点B后 , 就去判断是否移动B 。
B在旧 中的index=1 , 它的lastIndex=0 ,  不满足 index < lastIndex 的条件 , 因此 B 不做移动操作 。此时 , 一个操作是 , lastIndex=(index,lastIndex)中的较大数=1.
注意:lastIndex有点像浮标 , 或者说是一个map的索引 , 一开始默认值是0 , 它会与map中的元素进行比较 , 比较完后 , 会改变自己的值的(取index和lastIndex的较大数) 。
(2)看着 A , A在旧的index=0 , 此时的lastIndex=1(因为先前与新的B比较过了) ,  满足index<lastIndex  , 因此 , 对A进行移动操作 , 此时 lastIndex=max(index,lastIndex)=1 。
(3)看着D , 同(1) , 不移动 , 由于D在旧的index=3 , 比较时 , lastIndex=1 , 所以改变lastIndex=max(index,lastIndex)=3
(4)看着C , 同(2) , 移动 , C在旧的index=2 , 满足index<lastIndex(lastIndex=3) , 所以移动 。
由于C已经是最后一个节点 , 所以diff操作结束 。
情形二:新集合中有新加入的节点 , 旧集合中有删除的节点
diff算法介绍

文章插图
 
(1)B不移动 , 不赘述 , 更新l astIndex=1
(2)新集合取得 E , 发现旧不存在 , 故 在lastIndex=1的位置 创建E  , 更新lastIndex=1
(3)新集合取得C , C不移动 , 更新lastIndex=2
(4)新集合取得A , A移动 , 同上 , 更新lastIndex=2
(5) 新集合对比后 , 再对旧集合遍历 。判断 新集合 没有 , 但 旧集合 有的元素(如D , 新集合没有 , 旧集合有)  , 发现 D , 删除D , diff操作结束 。
diff的不足与待优化的地方
diff算法介绍

文章插图
 
看图的 D , 此时D不移动 , 但它的index是最大的 , 导致更新lastIndex=3 , 从而使得其他元素A,B,C的index<lastIndex , 导致A,B,C都要去移动 。
理想情况是只移动D , 不移动A,B,C 。因此 , 在开发过程中 , 尽量减少类似将最后一个节点移动到列表首部的操作 , 当节点数量过大或更新操作过于频繁时 , 会影响React的渲染性能 。

【diff算法介绍】


推荐阅读