updateContainer会调用 updateContainerAtExpirationTime ,初始更新是同步的所以expirationTime都是1073741823。updateContainerAtExpirationTime 传入的参数如下:
function updateContainer(element, container, parentComponent, callback){return updateContainerAtExpirationTime(element, container,parentComponent, expirationTime, callback);}
此时element是children;container是root(ReactRoot);parentComponent是null;expirationTime是1073741823;callback是work._onCommit。
在 updateContainerAtExpirationTime 中安排Root的更新,要注意的是current 是ReactRoot.current中的HostRootFiber。在这个方法中会对 ReactRoot的context、pendingContext属性中的一项进行修改。
export function updateContainerAtExpirationTime(element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,expirationTime: ExpirationTime,callback: ?Function,) {// TODO: If this is a nested container, this won't be the root.const current = container.current;const context = getContextForSubtree(parentComponent);if (container.context === null) {container.context = context;} else {container.pendingContext = context;}return scheduleRootUpdate(current, element, expirationTime, callback);}
执行到此,调用栈如下:updateContainer -> updateContainerAtExpirationTime -> scheduleRootUpdate。
scheduleRootUpdate
这个方法中是计划对Root的update,此时还没有开始work。
function scheduleRootUpdate(current: Fiber,element: ReactNodeList,expirationTime: ExpirationTime,callback: ?Function,) {const update = createUpdate(expirationTime);update.payload = {element}; //要更新的元素callback = callback === undefined ? null : callback;if (callback !== null) {update.callback = callback;}flushPassiveEffects();//刷新effectenqueueUpdate(current, update);//更新队列scheduleWork(current, expirationTime);//计划workreturn expirationTime;}//创建的是更新对象export function createUpdate(expirationTime: ExpirationTime): Update<*> {return {expirationTime: expirationTime,tag: UpdateState,//更新的标签payload: null,callback: null,next: null,nextEffect: null,};}
在enqueueUpdate 主要做的是append UpdateToQueue,队列传送门。若无队列则会createUpdateQueue,有队列则会cloneUpdateQueue。在此scheduleRootUpdate中是将update放入queue 。完成 flushPassiveEffects 和 enqueueUpdate 后就开始 scheduleWork。
scheduleWork
这个方法中会检查是否有working和,若有则中断fiber。markPendingPriorityLevel 中计算expirationTime和nextExpirationTimeToWorkOn,这有点 time slicing的味道。此处优先梳理Initial mount。
function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {const root = scheduleWorkToRoot(fiber, expirationTime);//根据fiber找rootif (root === null) { return; }if (!isWorking &&nextRenderExpirationTime !== NoWork &&expirationTime > nextRenderExpirationTime) {// This is an interruption. (Used for performance tracking.) //中断interruptedBy = fiber;resetStack();}markPendingPriorityLevel(root, expirationTime);//有点 time slicing 的味道if (// If we're in the render phase, we don't need to schedule this root// for an update, because we'll do it before we exit...!isWorking ||isCommitting ||// ...unless this is a different root than the one we're rendering.nextRoot !== root) {const rootExpirationTime = root.expirationTime;requestWork(root, rootExpirationTime);}if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {// Reset this back to zero so subsequent updates don't throw.nestedUpdateCount = 0;}}
scheduleWorkToRoot 主要目的是找到ReactRoot。同时为 fiber.expirationTime、 alternate.expirationTime添加过期时间,在查找ReactRoot的同时,为当前fiber.return的childExpirationTime 添加过期时间。在更新阶段,由于过期时间不为0,更新当前fiber。
requestWork
在requestWork 会判断是批处理、同步处理、按时间处理。请求 work,但并不一定可以work。isBatchingUpdates 时只是addRootToSchedule,但不进行work。addRootToSchedule 会建立一个链表,在findHighestPriorityRoot中会使用这个链表。Initial mount时,直接performSyncWork。
function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {addRootToSchedule(root, expirationTime);//添加到计划中if (isRendering) {// Prevent reentrancy. Remaining work will be scheduled at the end of// the currently rendering batch.return;}if (isBatchingUpdates) {// 事件是批处理----事件的批处理和此处有点关系// Flush work at the end of the batch.if (isUnbatchingUpdates) {// ...unless we're inside unbatchedUpdates, in which case we should// flush it now.nextFlushedRoot = root;nextFlushedExpirationTime = Sync;//过期时间变为同步performWorkOnRoot(root, Sync, false);}return;}if (expirationTime === Sync) {//初始时 expirationTime 是 SyncperformSyncWork();} else {scheduleCallbackWithExpirationTime(root, expirationTime);}}
init mount执行addRootToSchedule 后就开始执行 performSyncWork。performSyncWork 之后是:
performSyncWork —> performWork(Sync, false) —> findHighestPriorityRoot()、performWorkOnRoot ;
performWork
function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {findHighestPriorityRoot();//找到 addRootToSchedule 中存放的值//...while (nextFlushedRoot !== null &&nextFlushedExpirationTime !== NoWork &&minExpirationTime <= nextFlushedExpirationTime) {performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);findHighestPriorityRoot();}//...// Clean-up.finishRendering();}
findHighestPriorityRoot 找到 nextFlushedRoot 、nextFlushedExpirationTime,若没有则直接 finishRendering。在performWork中执行performWorkOnRoot是在while循环中,说明可能会有多个root。
performWorkOnRoot
function performWorkOnRoot(root: FiberRoot,expirationTime: ExpirationTime,isYieldy: boolean,) {isRendering = true; //开始rendering// Check if this is async work or sync/expired work.if (!isYieldy) {// Flush work without yielding.let finishedWork = root.finishedWork;if (finishedWork !== null) {// This root is already complete. We can commit it.completeRoot(root, finishedWork, expirationTime);} else {root.finishedWork = null;// If this root previously suspended, clear its existing timeout, since// we're about to try rendering again.const timeoutHandle = root.timeoutHandle;if (timeoutHandle !== noTimeout) {root.timeoutHandle = noTimeout;// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check abovecancelTimeout(timeoutHandle);}//*********************************************renderRoot(root, isYieldy);//生成React Tree ReconciliationfinishedWork = root.finishedWork;if (finishedWork !== null) {// We've completed the root. Commit it.completeRoot(root, finishedWork, expirationTime);}//*********************************************}}//...省略异步代码isRendering = false;}
在performWorkOnRoot 中开始rendering。若 finishedWork有值则直接 completeRoot,这是fibers的复用;若无finishedWork值则renderRoot -> completeRoot。在 performWorkOnRoot 中的 renderRoot 生成 React Tree Reconciliation,completeRoot 则是commit阶段。执行到此时,调用的栈为:
renderRoot
进入 renderRoot 中就进入working。之前scheduleWork在此正式开始。此处renderRoot代码只是摘要的,具体看renderRoot源码。
function renderRoot(root: FiberRoot, isYieldy: boolean): void {flushPassiveEffects();isWorking = true;var didFatal = false;startWorkLoopTimer(nextUnitOfWork);// Check if we're starting from a fresh stack, or if we're resuming from// previously yielded work.if (expirationTime !== nextRenderExpirationTime ||root !== nextRoot ||nextUnitOfWork === null) {// Reset the stack and start working from the root.resetStack();nextRoot = root;nextRenderExpirationTime = expirationTime;nextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime);root.pendingCommitExpirationTime = NoWork;}startWorkLoopTimer(nextUnitOfWork); //开始 "(React Tree Reconciliation)"do {try { workLoop(isYieldy); } catch (thrownValue) {//异常处理,此处省略 }break;} while (true);// We're done performing work. Time to clean up.isWorking = false;ReactCurrentDispatcher.current = null;resetContextDependences();resetHooks();// We completed the whole tree.const didCompleteRoot = true;stopWorkLoopTimer(interruptedBy, didCompleteRoot);// 删除 "(React Tree Reconciliation)"// Ready to commit.onComplete(root, rootWorkInProgress, expirationTime);}function onComplete(root, finishedWork, expirationTime) {root.pendingCommitExpirationTime = expirationTime;root.finishedWork = finishedWork;}
createWorkInProgress会创建fiber为current.alternate赋值并克隆当前fiber(HostRootFiber)的属性。在 startWorkLoopTimer中会标记”(React Tree Reconciliation)”,标志着开始生成React Tree Reconciliation;在 stopWorkLoopTimer 中会清除 “(React Tree Reconciliation)” 标记,标志着完成 React Tree Reconciliation。这中间就执行了WorkLoop方法。
workLoop示意图
workLoop中完成所有的work,并生成 React Tree 树。我们先来看一下workLoop的执行方式。此图有缺陷——就是假设子元素都是一次性生成的。 一般初始时fiber是RootFiber,由此开始生成React Tree。fiber是增量渲染,React Tree是逐渐生成的。
workLoop流程
我们假设当前有上图结构的树。
第一次执行时nextUnitOfWork是ReactRoot(fiberRooot),执行 performUnitOfWork -> beginWork生成a1,并返回a1
第二次执行时nextUnitOfWork 是a1,执行 performUnitOfWork -> beginWork 生成b1、b2、b3,并返回b1(next=b1);
第三次执行 nextUnitOfWork 是b1,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork (b2),并返回兄弟节点b2(next=b2);
第四次执行 nextUnitOfWork 是b2,执行performUnitOfWork -> beginWork 生成c1,并返回c1(next=c1);
第五次执行 nextUnitOfWork 是c1,执行performUnitOfWork -> beginWork 生成d1、d2,并返回d1(next=d1);
第六次执行 nextUnitOfWork 是d1,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork(d1),并返回兄弟节点d2(next=d2);
第七次执行 nextUnitOfWork 是d2,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork 结束当前d2的work。completeUnitOfWork是一个while循环,d2无兄弟节点,则workInProgress指向c1,并continue completeUnitOfWork循环,进入下一次while循环。接着的执行completeUnitOfWork -> completeWork(c1),c1无兄弟节点,则workInProgress指向b2,并continue completeUnitOfWork循环,进入下一次while循环;接着执行completeUnitOfWork -> completeWork(b2),并返回兄弟节点b3(next=b3);
第八次执行 nextUnitOfWork 是b3,执行 performUnitOfWork -> beginWork 生成c2,并返回c2(next=c2);
第九次执行 nextUnitOfWork 是c2,执行 performUnitOfWork -> beginWork,返回null,执行performUnitOfWork -> completeUnitOfWork -> completeWork 结束当前c2的work。completeUnitOfWork是一个while循环,c2无兄弟节点,则workInProgress指向b3,并continue completeUnitOfWork循环,进入下一次while循环。接着的执行completeUnitOfWork -> completeWork(b3),b3无兄弟节点,则workInProgress指向a1; 接着的执行completeUnitOfWork -> completeWork(a1),a1无兄弟节点也无父节点,则return null,结束循环;
这个流程只是说明workLoop的执行顺序,在实际渲染时渲染节点是需要区分节点类型,这些类型在beginWork中处理。
workLoop的代码
每一个ReactElement是一个fiber,每一fiber又是一个work。fiber的多少根jsx有关系,执行jsx时有多少个子元素就有多少个子fiber。performUnitOfWork 中返回的是next unit of work。
function workLoop(isYieldy) {if (!isYieldy) {// Flush work without yieldingwhile (nextUnitOfWork !== null) {nextUnitOfWork = performUnitOfWork(nextUnitOfWork);}} else {// Flush asynchronous work until there's a higher priority eventwhile (nextUnitOfWork !== null && !shouldYieldToRenderer()) {nextUnitOfWork = performUnitOfWork(nextUnitOfWork);}}}
performUnitOfWork 用于完成 Unit Of Work,同时并返回next,主要方法是beginWork。
function performUnitOfWork(workInProgress: Fiber): Fiber | null {// The current, flushed, state of this fiber is the alternate.// Ideally nothing should rely on this, but relying on it here// means that we don't need an additional field on the work in// progress.const current = workInProgress.alternate;//此处 alternate// See if beginning this work spawns more work.startWorkTimer(workInProgress);let next;if (enableProfilerTimer) {if (workInProgress.mode & ProfileMode) {startProfilerTimer(workInProgress);}next = beginWork(current, workInProgress, nextRenderExpirationTime);workInProgress.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;return next;}
传入beginWork中的 current 是 workInProgress.alternate,workInProgress是创建的fiber。

