React diff算法

192 阅读2分钟

前置

  • lastPlacedIndex: 最后一个 可复用的oldFiber的位置索引
  • oldFiber.index: 以 key值作为索引,对应的 oldFiber节点的位置索引,通过map快速获取

第一轮

  • 遍历新老,比对节点是否一致
    • key相同,type不同 时,会将老的Fiber节点标记为DELETION,但不会结束遍历
    • 当出现key不同, 新Fiber结点遍历完
  • 遍历结束会出现四种情况
    • newFiber遍历结束,oldFiber遍历结束:不需要第二轮遍历,对应位置无变化的场景
    • newFiber遍历结束,oldFiber有剩余节点:剩余的oldFiber节点标记为DELETION
    • newFiber有剩余节点,oldFiber遍历结束:剩余的newFiber节点作为新增节点
    • newFiber有剩余节点,oldFiber有剩余节点:进入diff

第二轮(diff)

  • 将还未处理的oldFiber节点存入了以自身节点的keykeyoldFiber本身 为valueMap
  • 开始遍历尚未处理的newFiber节点,开启比较
    • Map中存在newFiber节点
      • oldFiber.index >= lastPlacedIndex时,说明当前遍历的newFiber节点是可复用的 oldFiber节点,,所以不需要移动位置,此时lastPlacedIndex需要更新为oldFiber.index
      • oldFiber.index < lastPlacedIndex时,说明对应的oldFiber节点在oldFiber链表的相对位置是靠前的,需要标记为Placement,表明此节点需要移动,移动到前一个vnode节点的后面,此时 lastPlacedIndex无需更新
        • 为什么是这样移动的呢?
          • 首先列表是从头到尾遍历的。这就意味着对于当前VNode节点来说,该节点之前的所有节点都是排好序的,如果该节点需要移动,那么只需要将DOM节点移动到前一个VNode节点之后就可以
    • 如果map里找不到newFiber的key值,则直接创建新增节点

注意

react16及以后因为Fiber架构(由树转为了链表结构)后,协调器渲染器不再交替执行,而是协调器打标记,渲染器一次执行,不再多次执行渲染了