前置
- lastPlacedIndex: 最后一个 可复用的
oldFiber的位置索引 - oldFiber.index: 以 key值作为索引,对应的 oldFiber节点的位置索引,通过
map快速获取
第一轮
- 遍历新老,比对节点是否一致
- 当
key相同,type不同 时,会将老的Fiber节点标记为DELETION,但不会结束遍历 - 当出现
key不同, 新Fiber结点遍历完
- 当
- 遍历结束会出现四种情况
newFiber遍历结束,oldFiber遍历结束:不需要第二轮遍历,对应位置无变化的场景newFiber遍历结束,oldFiber有剩余节点:剩余的oldFiber节点标记为DELETIONnewFiber有剩余节点,oldFiber遍历结束:剩余的newFiber节点作为新增节点newFiber有剩余节点,oldFiber有剩余节点:进入diff
第二轮(diff)
- 将还未处理的
oldFiber节点存入了以自身节点的key为key,oldFiber本身 为value的Map中 - 开始遍历尚未处理的
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值,则直接创建新增节点
- 当Map中存在
注意
react16及以后因为Fiber架构(由树转为了链表结构)后,协调器和渲染器不再交替执行,而是协调器打标记,渲染器一次执行,不再多次执行渲染了