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
调用 ReactDOM.render App 组件时会创建 FiberRoot -> createHostRootFiber,标记这个 fiber的 tag 为 HostRoot,创建 FiberRoot 的同时创建 RootFiber,对应 ReactDOM.render 方法的第二个参数对应的 Dom 节点
functioncreateFiberRoot(containerInfo: any,isConcurrent: boolean,hydrate: boolean,): FiberRoot{// Cyclic construction. This cheats the type system right now because// stateNode is any.constuninitializedFiber=createHostRootFiber(isConcurrent);// ....}functioncreateHostRootFiber(isConcurrent: boolean): Fiber{letmode=isConcurrent ? ConcurrentMode|StrictMode : NoContext;if(enableProfilerTimer&&isDevToolsPresent){// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode|=ProfileMode;}returncreateFiber(HostRoot,null,null,mode);}
第一次执行到 beginWork 时首次更新的是 HostRoot,在 updateHostRoot 中会调用 reconcileChildFibers 调和子节点,在reconcileSingleElement -> createFiberFromElement -> createFiberFromTypeAndProps依次将 children element 创建成对应的 Fiber 对象, 默认 fiberTag 为 IndeterminateComponent,根据 React.createElement 返回的 type 将 fiberTag 标记为对应的组件类型,将创建完的 children 返回到 workLoop,遍历执行 performUnitOfWork -> beginWork, 根据这个 children 的 fiberTag 对应依次更新,如此循环先创建,再更新。
functionrenderRoot(root: FiberRoot,isYieldy: boolean,isExpired: boolean,): void{isWorking=true;ReactCurrentOwner.currentDispatcher=Dispatcher;constexpirationTime=root.nextExpirationTimeToWorkOn;// Check if we're starting from a fresh stack, or if we're resuming from// previously yielded work.// 说明将要执行的任务 root 和 expirationTime 和 nextRenderExpirationTime、nextRoot 预期的不一样, 可能是之前任务被高优先级的任务打断了。if(expirationTime!==nextRenderExpirationTime||root!==nextRoot||nextUnitOfWork===null// 更新结束 fiber 的 child,下一个节点, 首次为 null){// Reset the stack and start working from the root.// 重置resetStack();nextRoot=root;// root.nextExpirationTimeToWorkOn;nextRenderExpirationTime=expirationTime;// 将 fiber 拷贝成 workInProgress 对象操作,不直接操作 fiber 节点nextUnitOfWork=createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime,);root.pendingCommitExpirationTime=NoWork;letdidFatal=false;startWorkLoopTimer(nextUnitOfWork);// 开始循环 workLoopdo{try{// 同步或过期任务则 isYieldy 为 false 表示不可中断workLoop(isYieldy);}catch(thrownValue){// ... catch 错误// 致命错误if(didFatal){}// workLoop break 中断的错误if(nextUnitOfWork!==null){}// 可处理的错误if(nextRenderDidError){}}break;// 遇到了某种错误跳出}while(true);// ...constrootWorkInProgress=root.current.alternate;// Ready to commit.// 最后 commit rootonComplete(root,rootWorkInProgress,expirationTime);}
workLoop
根据 isYieldy 不同执行 performUnitOfWork
functionworkLoop(isYieldy){// 同步或过期任务 isYieldy 为false 不可中断if(!isYieldy){// Flush work without yieldingwhile(nextUnitOfWork!==null){nextUnitOfWork=performUnitOfWork(nextUnitOfWork);}}else{// Flush asynchronous work until the deadline runs out of time.// !shouldYield() 还有剩余时间用来更新while(nextUnitOfWork!==null&&!shouldYield()){nextUnitOfWork=performUnitOfWork(nextUnitOfWork);}}}
performUnitOfWork
next 变量赋值为 beginWork 的返回值,即更新完之后会返回它的下一个节点
functionperformUnitOfWork(workInProgress: Fiber): Fiber|null{constcurrent=workInProgress.alternate;// See if beginning this work spawns more work.startWorkTimer(workInProgress);letnext;if(enableProfilerTimer){if(workInProgress.mode&ProfileMode){startProfilerTimer(workInProgress);}// 执行对整个树每个节点进行更新的操作,赋值 next 为更新完后的下一个节点next=beginWork(current,workInProgress,nextRenderExpirationTime);// 将最新的 props 赋值给目前正在用的 propsworkInProgress.memoizedProps=workInProgress.pendingProps;if(workInProgress.mode&ProfileMode){// Record the render duration assuming we didn't bailout (or error).stopProfilerTimerIfRunningAndRecordDelta(workInProgress,true);}}else{next=beginWork(current,workInProgress,nextRenderExpirationTime);workInProgress.memoizedProps=workInProgress.pendingProps;}// 更新到最后的节点,叶子节点if(next===null){// If this doesn't spawn new work, complete the current work.// 往上遍历next=completeUnitOfWork(workInProgress);}ReactCurrentOwner.current=null;returnnext;}
beginWork
判断当前 fiber 树是否需要更新,不需要更新的则进行优化
根据节点类型进行对应的更新
更新后调和子节点
functionbeginWork(// RootFibercurrent: Fiber|null,// 与 nextUnitOfWork 对应,下一个将要更新的节点workInProgress: Fiber,// root.nextExpirationTimeToWorkOn 表示一次更新内最高优先级的更新renderExpirationTime: ExpirationTime,): Fiber|null{// 节点自身的过期时间constupdateExpirationTime=workInProgress.expirationTime;if(current!==null){constoldProps=current.memoizedProps;constnewProps=workInProgress.pendingProps;if(// 前后两次props 相同oldProps===newProps&&// context 相关!hasLegacyContextChanged()&&// 没有更新(updateExpirationTime===NoWork||// 更新优先级没有当前更新优先级高updateExpirationTime>renderExpirationTime)){// This fiber does not have any pending work. Bailout without entering// the begin phase. There's still some bookkeeping we that needs to be done// in this optimized path, mostly pushing stuff onto the stack.switch(workInProgress.tag){// ...优化的过程}// 跳过当前节点及其子节点的更新returnbailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}}// Before entering the begin phase, clear the expiration time.workInProgress.expirationTime=NoWork;// 节点是有更新的情况switch(workInProgress.tag){// ...根据节点类型,执行对应组件类型的更新}}
functionbailoutOnAlreadyFinishedWork(current: Fiber|null,workInProgress: Fiber,renderExpirationTime: ExpirationTime,): Fiber|null{cancelWorkTimer(workInProgress);if(current!==null){// Reuse previous context listworkInProgress.firstContextDependency=current.firstContextDependency;}// Check if the children have any pending work.// 子节点最高优先级的更新constchildExpirationTime=workInProgress.childExpirationTime;if(// 没有更新childExpirationTime===NoWork||// 子树的优先级低childExpirationTime>renderExpirationTime){// The children don't have any work either. We can skip them.// TODO: Once we add back resuming, we should check if the children are// a work-in-progress set. If so, we need to transfer their effects.// 直接跳过子树更新returnnull;}else{// This fiber doesn't have work, but its subtree does. Clone the child// fibers and continue.// 子树有更新的情况// 当前节点不需要更新,说明 child 没有更新,把老的 workInProgress.child 复制一份,并返回cloneChildFibers(current,workInProgress);returnworkInProgress.child;}}
functionscheduleRootUpdate(current: Fiber,// RootFiberelement: ReactNodeList,// ReactDOM.render 的第一个参数expirationTime: ExpirationTime,callback: ?Function,){// 创建 update,这个update tag 为 UpdateStateconstupdate=createUpdate(expirationTime);// Caution: React DevTools currently depends on this property// being called "element".// 这里的 payload 相当于 state ,跟 setState 调用效果相同update.payload={element};callback=callback===undefined ? null : callback;if(callback!==null){warningWithoutStack(typeofcallback==='function','render(...): Expected the last optional `callback` argument to be a '+'function. Instead received: %s.',callback,);update.callback=callback;}// 为 RootFiber 添加 updateQueueenqueueUpdate(current,update);scheduleWork(current,expirationTime);returnexpirationTime;}
更新过程, state 里只有一个属性,即上面方法的 payload,通过 processUpdateQueue 得到一个 element 属性新 state ,从这个 element 开始创建和更新整个 fiber tree 。
functionupdateHostRoot(current,workInProgress,renderExpirationTime){pushHostRootContext(workInProgress);constupdateQueue=workInProgress.updateQueue;invariant(updateQueue!==null,'If the root does not have an updateQueue, we should have already '+'bailed out. This error is likely caused by a bug in React. Please '+'file an issue.',);constnextProps=workInProgress.pendingProps;constprevState=workInProgress.memoizedState;// 首次 prevState 为nullconstprevChildren=prevState!==null ? prevState.element : null;// 执行 processUpdateQueue 将 updateQueue 执行得到有个 element 属性的新的state, processUpdateQueue(workInProgress,updateQueue,nextProps,null,renderExpirationTime,);// 拿到 scheduleRootUpdate 时的 update.payload constnextState=workInProgress.memoizedState;// Caution: React DevTools currently depends on this property// being called "element".// 拿到 element 即 <App />constnextChildren=nextState.element;// 如果相同,则跳过更新,// 一般不会在 RootFiber 上创建更新,而是在 ReactDOM.render 传入的第一个参数那个节点(app)上创建更新if(nextChildren===prevChildren){// If the state is the same as before, that's a bailout because we had// no work that expires at this time.// 服务端渲染相关,复用节点resetHydrationState();returnbailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}constroot: FiberRoot=workInProgress.stateNode;// 服务端渲染相关if((current===null||current.child===null)&&root.hydrate&&enterHydrationState(workInProgress)){workInProgress.effectTag|=Placement;// 认为这是第一次渲染workInProgress.child=mountChildFibers(workInProgress,null,nextChildren,renderExpirationTime,);}else{// Otherwise reset hydration state in case we aborted and resumed another// root.// ReactDOM.render -> 首次渲染时,current 和 workInProgress 都是存在的, 那么会走 reconcileChildFibers 更新调和reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);resetHydrationState();}returnworkInProgress.child;}
The text was updated successfully, but these errors were encountered:
renderRoot
ReactDOM.render App
组件时会创建FiberRoot -> createHostRootFiber
,标记这个 fiber的 tag 为HostRoot
,创建FiberRoot
的同时创建RootFiber
,对应ReactDOM.render
方法的第二个参数对应的 Dom 节点beginWork
时首次更新的是HostRoot
,在updateHostRoot
中会调用reconcileChildFibers
调和子节点,在reconcileSingleElement -> createFiberFromElement -> createFiberFromTypeAndProps
依次将children element
创建成对应的Fiber
对象, 默认fiberTag
为IndeterminateComponent
,根据React.createElement
返回的type
将fiberTag
标记为对应的组件类型,将创建完的children
返回到workLoop
,遍历执行performUnitOfWork -> beginWork
, 根据这个children
的fiberTag
对应依次更新,如此循环先创建,再更新。workLoop
进行循环单元更新, 对整棵 fiberTree 都遍历一遍nextUnitOfWork
是每个节点自己更新完之后返回的第一个子节点nextUnitOfWork
首次赋值为createWorkInProgress
拷贝的一份fiber
节点,以后的操作都是修改的 nextUnitOfWork, 防止改变当前 fiberTreeworkLoop
performUnitOfWork
beginWork
fiber
树是否需要更新,不需要更新的则进行优化bailoutOnAlreadyFinishedWork
fiber
的子树是否需要更新,如果需要更新则会 clone 一份老的 child 到workInProgress.child
返回到performUnitOfWork
的next
再返回到workLoop
的nextUnitOfWork
循环更新子节点, 否则为return null
根据 fiber 的 tag 类型进行更新
fiber
的expirationTIme
置为Nowork
workInProgress.type
函数式组件为function``,
class组件为
class` 构造函数,dom原生组件为div这种字符串penddingProps
,新的渲染产生的props
updateHostRoot
一个 React 应用中一般只有一个
hostRoot
,对应的Fiber
为RootFiber
对象, 对应的element
为ReactDOM.render
方法的第二个参数<div id="root" />
, 也就是 FiberRoot。创建更新的过程,
ReactDOM.render
->ReactRoot.prototype.render
->DOMRenderer.updateContainer
->updateContainerAtExpirationTime
->scheduleRootUpdate
processUpdateQueue
得到一个element
属性新state
,从这个 element 开始创建和更新整个 fiber tree 。The text was updated successfully, but these errors were encountered: