You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionreconcileChildrenArray(// current fiber 父节点returnFiber: Fiber,// current fiber 的第一个child, 其余child用sibling指针链接 (父节点没有指向非首个子节点的指针)currentFirstChild: Fiber|null,// 当前要更新的child 数组newChildren: Array<*>,expirationTime: ExpirationTime,): Fiber|null{// 需要返回的结果,当返回时,已经是匹配复用好了的fiber first child,其余child用sibling指针链接letresultingFirstChild: Fiber|null=null;letpreviousNewFiber: Fiber|null=null;letoldFiber=currentFirstChild;// 最后一个可复用节点的index, index指的是current fiber child(oldFiber)对应的indexletlastPlacedIndex=0;letnewIdx=0;letnextOldFiber=null;// newChildren第一次循环// 处理的是key相同,并且是按顺序的,也就是newChildren 的节点没有改变位置// 这一次的循环处理完后,有如下结果// 1. newChildren没有遍历完,oldFiber也没有遍历完,待后续处理// 2. newChildren 数据已经匹配处理完成, 那就要标记删除oldFiber的节点// 3. 没有oldFiber可以复用,那就要新建插入fiberfor(;oldFiber!==null&&newIdx<newChildren.length;newIdx++){if(oldFiber.index>newIdx){nextOldFiber=oldFiber;oldFiber=null;}else{nextOldFiber=oldFiber.sibling;}// key相等就复用oldFiberconstnewFiber=updateSlot(returnFiber,oldFiber,newChildren[newIdx],expirationTime,);if(newFiber===null){if(oldFiber===null){oldFiber=nextOldFiber;}break;}// shouldTrackSideEffects 非首屏渲染为true, 表示需要标记 workInProgress child的effectTagif(shouldTrackSideEffects){// newFiber.alternate ===null 匹配完成if(oldFiber&&newFiber.alternate===null){//标记删除其余的old fiber childdeleteChild(returnFiber,oldFiber);}}lastPlacedIndex=placeChild(newFiber,lastPlacedIndex,newIdx);if(previousNewFiber===null){resultingFirstChild=newFiber;}else{previousNewFiber.sibling=newFiber;}// 记录能匹配到的最后一个newFiber childpreviousNewFiber=newFiber;// 记录能匹配到的最后一个oldFiber childoldFiber=nextOldFiber;}// newChildren 数据已经匹配处理完成if(newIdx===newChildren.length){// 标记删除其余的old fiber childdeleteRemainingChildren(returnFiber,oldFiber);returnresultingFirstChild;}if(oldFiber===null){// 没有oldFiber可以复用,新建插入fiberfor(;newIdx<newChildren.length;newIdx++){constnewFiber=createChild(returnFiber,newChildren[newIdx],expirationTime,);if(newFiber===null){continue;}lastPlacedIndex=placeChild(newFiber,lastPlacedIndex,newIdx);if(previousNewFiber===null){// TODO: Move out of the loop. This only happens for the first run.resultingFirstChild=newFiber;}else{// 用sibling串联newFiberpreviousNewFiber.sibling=newFiber;}previousNewFiber=newFiber;}returnresultingFirstChild;}// Add all children to a key map for quick lookups.// 处理oldFiber, 转换为一个map, key为oldFiber节点的key, value为oldFiber childconstexistingChildren=mapRemainingChildren(returnFiber,oldFiber);// newChildren第二次循环开始// 处理的是newChildren的节点位置换了// 这里的逻辑有点绕,需要举🌰for(;newIdx<newChildren.length;newIdx++){constnewFiber=updateFromMap(existingChildren,returnFiber,newIdx,newChildren[newIdx],expirationTime,);if(newFiber!==null){if(shouldTrackSideEffects){if(newFiber.alternate!==null){// The new fiber is a work in progress, but if there exists a// current, that means that we reused the fiber. We need to delete// it from the child list so that we don't add it to the deletion// list.existingChildren.delete(newFiber.key===null ? newIdx : newFiber.key,);}}lastPlacedIndex=placeChild(newFiber,lastPlacedIndex,newIdx);if(previousNewFiber===null){resultingFirstChild=newFiber;}else{previousNewFiber.sibling=newFiber;}previousNewFiber=newFiber;}}if(shouldTrackSideEffects){// Any existing children that weren't consumed above were deleted. We need// to add them to the deletion list.existingChildren.forEach((child)=>deleteChild(returnFiber,child));}returnresultingFirstChild;}
react Diff 算法
react Diff 操作在哪个阶段?
react Diff 操作发生在render阶段产出workInProgress fiber节点的时候,会根据current fiber tree 和本次要更新节点进行diff,同时会标记effect tag(在effect list fiber tree 上)。
reconcileChildFibers就是diff算法的入口函数
react Diff 是如何做优化的?
正常的两棵树diff的时间复杂度是O(n^3) ,在React官网文档Reconciliation上有提到。
O(n^3) 的大致由来: 两棵树嵌套循环寻找不同的节点:O(n^2),寻找到不同的节点后,需要再遍历得到最小的转换消耗,最终得 到O(n^3)
React基于两个假设实现了一种启发式O(n)算法
按照这样的优化,最终只需一层遍历O(n)即可完成
react Diff 源码浅析
通过判断新节点(newChild)的类型,匹配不同的diff操作。
最后对没有匹配的到newChild节点的old子节点进行删除操作
reconcileSingleElement
详细注释
reconcileChildrenArray
详细注释
第二次循环的例子(🌰来源)
The text was updated successfully, but these errors were encountered: