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
functionreconcileChildren(current: Fiber|null,workInProgress: Fiber,nextChildren: any,renderExpirationTime: ExpirationTime,){// 首次渲染if(current===null){// If this is a fresh new component that hasn't been rendered yet, we// won't update its child set by applying minimal side-effects. Instead,// we will add them all to the child before it gets rendered. That means// we can optimize this reconciliation pass by not tracking side-effects.workInProgress.child=mountChildFibers(workInProgress,null,nextChildren,renderExpirationTime,);}else{// If the current child is the same as the work in progress, it means that// we haven't yet started any work on these children. Therefore, we use// the clone algorithm to create a copy of all the current children.// If we had any progressed work already, that is invalid at this point so// let's throw it out.// 更新渲染workInProgress.child=reconcileChildFibers(workInProgress,current.child,// 再次渲染时才会有子节点nextChildren,renderExpirationTime,);}}// 更新渲染exportconstreconcileChildFibers=ChildReconciler(true);// 首次渲染exportconstmountChildFibers=ChildReconciler(false);
reconcileChildFibers
reconcileChildFibers 是 ChildReconciler 最终返回的函数
先判断 newChild 是不是 Fragment 节点,如果是 Fragment 则将 newChild 赋值为 newChild.props.children
接着判断 newChild 是不是 isObject
REACT_ELEMENT_TYPE:reconcileSingleElement
REACT_PORTAL_TYPE: reconcileSinglePortal
判断 string or number:reconcileSingleTextNode
判断 isArray:reconcileChildrenArray
判断是 iterator 函数: reconcileChildrenIterator
都不符合以上情况,并且还是 isObject,则抛出错误
newChild 为 undefined 并且非 Fragment,提醒没有返回值
为 null 的情况,新的 props 的 children 为 null,则把现有的所有 children 都删掉
functionreconcileChildrenArray(returnFiber: Fiber,currentFirstChild: Fiber|null,newChildren: Array<*>,expirationTime: ExpirationTime,): Fiber|null{// 要返回的结果letresultingFirstChild: Fiber|null=null;// 用于构建链结构的中间变量letpreviousNewFiber: Fiber|null=null;// 上一次渲染完成后第一个 child 节点, current.childletoldFiber=currentFirstChild;letlastPlacedIndex=0;letnewIdx=0;// 下一个老的节点letnextOldFiber=null;for(;oldFiber!==null&&newIdx<newChildren.length;newIdx++){// 老的 fiber 的 index 位置与新的不同,位置不匹配if(oldFiber.index>newIdx){// 将 oldFiber 赋值给 nextOldFiber 暂存nextOldFiber=oldFiber;oldFiber=null;}else{// 没变化则下一个老的节点为当前的节点的兄弟节点nextOldFiber=oldFiber.sibling;}// constnewFiber=updateSlot(returnFiber,oldFiber,newChildren[newIdx],expirationTime,);// key 相同根据类型确定复用类型// 文本节点没有key,为 null 表示不能复用, 能复用也有复用复用节点和新建节点的区分 if(newFiber===null){// TODO: This breaks on empty slots like null children. That's// unfortunate because it triggers the slow path all the time. We need// a better way to communicate whether this was a miss or null,// boolean, undefined, etc.// 重置 oldFiber,跳出循环,即找到不能复用的那个节点为止的fiber,那么 newIdx 则表示有多少个节点相同的那个indexif(oldFiber===null){oldFiber=nextOldFiber;}break;}// 更新渲染的情况if(shouldTrackSideEffects){// 没有复用,说明这是这是创建新的节点, 则要标记删除老的if(oldFiber&&newFiber.alternate===null){// We matched the slot, but we didn't reuse the existing fiber, so we// need to delete the existing child.deleteChild(returnFiber,oldFiber);}}lastPlacedIndex=placeChild(newFiber,lastPlacedIndex,newIdx);// previousNewFiber 中间变量,将数组构建成链表结构if(previousNewFiber===null){// TODO: Move out of the loop. This only happens for the first run.// 首个子节点resultingFirstChild=newFiber;}else{// TODO: Defer siblings if we're not at the right index for this slot.// I.e. if we had null values before, then we want to defer this// for each null value. However, we also don't want to call updateSlot// with the previous one.// 构建链表结构previousNewFiber.sibling=newFiber;}previousNewFiber=newFiber;oldFiber=nextOldFiber;}// 新数组操作完,所有新的 children 都全部创建 fiber 节点了if(newIdx===newChildren.length){// We've reached the end of the new children. We can delete the rest.// 删除老的deleteRemainingChildren(returnFiber,oldFiber);// 返回首个子节点,其他节点是通过 sibling 指向下去returnresultingFirstChild;}if(oldFiber===null){// If we don't have any more existing children we can choose a fast path// since the rest will all be insertions.// 老的节点被复用完了,遍历到最后没有能复用的节点了,新的还有部分没创建, 则直接创建新的for(;newIdx<newChildren.length;newIdx++){constnewFiber=createChild(returnFiber,newChildren[newIdx],expirationTime,);if(!newFiber){continue;}// 同样 placeChild 标记移动位置lastPlacedIndex=placeChild(newFiber,lastPlacedIndex,newIdx);if(previousNewFiber===null){// TODO: Move out of the loop. This only happens for the first run.resultingFirstChild=newFiber;}else{// 构建链表结构previousNewFiber.sibling=newFiber;}previousNewFiber=newFiber;}returnresultingFirstChild;}// Add all children to a key map for quick lookups.// newChildren 还没创建完,oldFiber 还不为null// 数组存在顺序的变化,在 oldFiber 里找到可以复用的,通过 map 快速寻找// 根据 key 或 index 创建 map 对象constexistingChildren=mapRemainingChildren(returnFiber,oldFiber);// 一次完整的遍历// Keep scanning and use the map to restore deleted items as moves.for(;newIdx<newChildren.length;newIdx++){// 根据 map 找可以复用的节点 或 创建新节点,跟 updateSlot 相似constnewFiber=updateFromMap(existingChildren,returnFiber,newIdx,newChildren[newIdx],expirationTime,);if(newFiber){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.// 从 map 里删除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;}
The text was updated successfully, but these errors were encountered:
函数式组件(FunctionComponent)更新过程
updateFunctionComponent
reconcileChildren
fiber.children
生成fiber
子树Fiber
对象是否可以复用,在第一次渲染就渲染了fiber
子树,state
变化可能会导致某些子节点产生变化而不能复用,但是大部分是可以复用的key
优化reconcileChildFibers
reconcileChildFibers
是ChildReconciler
最终返回的函数newChild
是不是Fragment
节点,如果是Fragment
则将newChild
赋值为newChild.props.children
newChild
是不是isObject
REACT_ELEMENT_TYPE
:reconcileSingleElement
REACT_PORTAL_TYPE
:reconcileSinglePortal
string or number
:reconcileSingleTextNode
isArray
:reconcileChildrenArray
iterato
r 函数:reconcileChildrenIterator
isObject
,则抛出错误newChild
为undefined
并且非Fragment
,提醒没有返回值null
的情况,新的props
的children
为null
,则把现有的所有children
都删掉React Element 类型——reconcileSingleElement
elementType
来创建新的节点useFiber 复用节点
deleteChild 标记删除 和 deleteRemainingChildren
数组类型——reconcileChildrenArray
尽量减少数组的遍历次数来达到判断节点是否可复用的过程
第一次遍历
newIdx
则为能够有几个能够复用的节点的index
updateSlot
中通过判断新老Key
是否相同来复用updateSlot
返回null
表示不能复用, 直接break
oldFiber
有key
,而newChild
是textNode
直接返回null
, 因为文本节点是没有Key
的textNode、Fragment、Element
都会判断oldFiber
不为null
就复用,为null
则创建新的第一次遍历完 或者
break
后newIdx
等于newChildren.length
说明已经在updateSlot
中创建新的对象了, 新数组操作完成了, 所有新节点都已经创建oldFiber
oldFiber
为null
,老的节点被复用完了,遍历到最后没有能复用的节点了,新的还有部分没创建, 则直接创建新的最后
newChildren
还没创建完,oldFiber
还不为null
的情况,数组存在顺序的变化updateFromMap
: 根据key
或index
创建map
对象,通过map
对象在oldFiber
里找到可以复用的或者创建新的The text was updated successfully, but these errors were encountered: