updateContainer会调用 updateContainerAtExpirationTime ,初始更新是同步的所以expirationTime都是1073741823。updateContainerAtExpirationTime 传入的参数如下:

  1. function updateContainer(element, container, parentComponent, callback){
  2. return updateContainerAtExpirationTime(element, container,
  3. parentComponent, expirationTime, callback);
  4. }

此时element是children;container是root(ReactRoot);parentComponent是null;expirationTime是1073741823;callback是work._onCommit。

在 updateContainerAtExpirationTime 中安排Root的更新,要注意的是current 是ReactRoot.current中的HostRootFiber。在这个方法中会对 ReactRoot的context、pendingContext属性中的一项进行修改。

  1. export function updateContainerAtExpirationTime(
  2. element: ReactNodeList,
  3. container: OpaqueRoot,
  4. parentComponent: ?React$Component<any, any>,
  5. expirationTime: ExpirationTime,
  6. callback: ?Function,
  7. ) {
  8. // TODO: If this is a nested container, this won't be the root.
  9. const current = container.current;
  10. const context = getContextForSubtree(parentComponent);
  11. if (container.context === null) {
  12. container.context = context;
  13. } else {
  14. container.pendingContext = context;
  15. }
  16. return scheduleRootUpdate(current, element, expirationTime, callback);
  17. }

执行到此,调用栈如下:updateContainer -> updateContainerAtExpirationTime -> scheduleRootUpdate。

scheduleRootUpdate

这个方法中是计划对Root的update,此时还没有开始work。

  1. function scheduleRootUpdate(
  2. current: Fiber,
  3. element: ReactNodeList,
  4. expirationTime: ExpirationTime,
  5. callback: ?Function,
  6. ) {
  7. const update = createUpdate(expirationTime);
  8. update.payload = {element}; //要更新的元素
  9. callback = callback === undefined ? null : callback;
  10. if (callback !== null) {
  11. update.callback = callback;
  12. }
  13. flushPassiveEffects();//刷新effect
  14. enqueueUpdate(current, update);//更新队列
  15. scheduleWork(current, expirationTime);//计划work
  16. return expirationTime;
  17. }
  18. //创建的是更新对象
  19. export function createUpdate(expirationTime: ExpirationTime): Update<*> {
  20. return {
  21. expirationTime: expirationTime,
  22. tag: UpdateState,//更新的标签
  23. payload: null,
  24. callback: null,
  25. next: null,
  26. nextEffect: null,
  27. };
  28. }

在enqueueUpdate 主要做的是append UpdateToQueue,队列传送门。若无队列则会createUpdateQueue,有队列则会cloneUpdateQueue。在此scheduleRootUpdate中是将update放入queue 。完成 flushPassiveEffects 和 enqueueUpdate 后就开始 scheduleWork。

scheduleWork

这个方法中会检查是否有working和,若有则中断fiber。markPendingPriorityLevel 中计算expirationTime和nextExpirationTimeToWorkOn,这有点 time slicing的味道。此处优先梳理Initial mount。

  1. function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
  2. const root = scheduleWorkToRoot(fiber, expirationTime);//根据fiber找root
  3. if (root === null) { return; }
  4. if (!isWorking &&
  5. nextRenderExpirationTime !== NoWork &&
  6. expirationTime > nextRenderExpirationTime
  7. ) {
  8. // This is an interruption. (Used for performance tracking.) //中断
  9. interruptedBy = fiber;
  10. resetStack();
  11. }
  12. markPendingPriorityLevel(root, expirationTime);//有点 time slicing 的味道
  13. if (
  14. // If we're in the render phase, we don't need to schedule this root
  15. // for an update, because we'll do it before we exit...
  16. !isWorking ||
  17. isCommitting ||
  18. // ...unless this is a different root than the one we're rendering.
  19. nextRoot !== root
  20. ) {
  21. const rootExpirationTime = root.expirationTime;
  22. requestWork(root, rootExpirationTime);
  23. }
  24. if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
  25. // Reset this back to zero so subsequent updates don't throw.
  26. nestedUpdateCount = 0;
  27. }
  28. }

scheduleWorkToRoot 主要目的是找到ReactRoot。同时为 fiber.expirationTime、 alternate.expirationTime添加过期时间,在查找ReactRoot的同时,为当前fiber.return的childExpirationTime 添加过期时间。在更新阶段,由于过期时间不为0,更新当前fiber。

requestWork

在requestWork 会判断是批处理、同步处理、按时间处理。请求 work,但并不一定可以work。isBatchingUpdates 时只是addRootToSchedule,但不进行work。addRootToSchedule 会建立一个链表,在findHighestPriorityRoot中会使用这个链表。Initial mount时,直接performSyncWork。

  1. function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
  2. addRootToSchedule(root, expirationTime);//添加到计划中
  3. if (isRendering) {
  4. // Prevent reentrancy. Remaining work will be scheduled at the end of
  5. // the currently rendering batch.
  6. return;
  7. }
  8. if (isBatchingUpdates) {// 事件是批处理----事件的批处理和此处有点关系
  9. // Flush work at the end of the batch.
  10. if (isUnbatchingUpdates) {
  11. // ...unless we're inside unbatchedUpdates, in which case we should
  12. // flush it now.
  13. nextFlushedRoot = root;
  14. nextFlushedExpirationTime = Sync;//过期时间变为同步
  15. performWorkOnRoot(root, Sync, false);
  16. }
  17. return;
  18. }
  19. if (expirationTime === Sync) {//初始时 expirationTime 是 Sync
  20. performSyncWork();
  21. } else {
  22. scheduleCallbackWithExpirationTime(root, expirationTime);
  23. }
  24. }

init mount执行addRootToSchedule 后就开始执行 performSyncWork。performSyncWork 之后是:

performSyncWork —> performWork(Sync, false) —> findHighestPriorityRoot()、performWorkOnRoot ;

performWork

  1. function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
  2. findHighestPriorityRoot();//找到 addRootToSchedule 中存放的值
  3. //...
  4. while (
  5. nextFlushedRoot !== null &&
  6. nextFlushedExpirationTime !== NoWork &&
  7. minExpirationTime <= nextFlushedExpirationTime
  8. ) {
  9. performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
  10. findHighestPriorityRoot();
  11. }
  12. //...
  13. // Clean-up.
  14. finishRendering();
  15. }

findHighestPriorityRoot 找到 nextFlushedRoot 、nextFlushedExpirationTime,若没有则直接 finishRendering。在performWork中执行performWorkOnRoot是在while循环中,说明可能会有多个root。

performWorkOnRoot

  1. function performWorkOnRoot(
  2. root: FiberRoot,
  3. expirationTime: ExpirationTime,
  4. isYieldy: boolean,
  5. ) {
  6. isRendering = true; //开始rendering
  7. // Check if this is async work or sync/expired work.
  8. if (!isYieldy) {
  9. // Flush work without yielding.
  10. let finishedWork = root.finishedWork;
  11. if (finishedWork !== null) {
  12. // This root is already complete. We can commit it.
  13. completeRoot(root, finishedWork, expirationTime);
  14. } else {
  15. root.finishedWork = null;
  16. // If this root previously suspended, clear its existing timeout, since
  17. // we're about to try rendering again.
  18. const timeoutHandle = root.timeoutHandle;
  19. if (timeoutHandle !== noTimeout) {
  20. root.timeoutHandle = noTimeout;
  21. // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
  22. cancelTimeout(timeoutHandle);
  23. }
  24. //*********************************************
  25. renderRoot(root, isYieldy);//生成React Tree Reconciliation
  26. finishedWork = root.finishedWork;
  27. if (finishedWork !== null) {
  28. // We've completed the root. Commit it.
  29. completeRoot(root, finishedWork, expirationTime);
  30. }
  31. //*********************************************
  32. }
  33. }
  34. //...省略异步代码
  35. isRendering = false;
  36. }

在performWorkOnRoot 中开始rendering。若 finishedWork有值则直接 completeRoot,这是fibers的复用;若无finishedWork值则renderRoot -> completeRoot。在 performWorkOnRoot 中的 renderRoot 生成 React Tree Reconciliation,completeRoot 则是commit阶段。执行到此时,调用的栈为:
image.png

renderRoot

进入 renderRoot 中就进入working。之前scheduleWork在此正式开始。此处renderRoot代码只是摘要的,具体看renderRoot源码

  1. function renderRoot(root: FiberRoot, isYieldy: boolean): void {
  2. flushPassiveEffects();
  3. isWorking = true;
  4. var didFatal = false;
  5. startWorkLoopTimer(nextUnitOfWork);
  6. // Check if we're starting from a fresh stack, or if we're resuming from
  7. // previously yielded work.
  8. if (
  9. expirationTime !== nextRenderExpirationTime ||
  10. root !== nextRoot ||
  11. nextUnitOfWork === null
  12. ) {
  13. // Reset the stack and start working from the root.
  14. resetStack();
  15. nextRoot = root;
  16. nextRenderExpirationTime = expirationTime;
  17. nextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime);
  18. root.pendingCommitExpirationTime = NoWork;
  19. }
  20. startWorkLoopTimer(nextUnitOfWork); //开始 "(React Tree Reconciliation)"
  21. do {
  22. try { workLoop(isYieldy); } catch (thrownValue) {//异常处理,此处省略 }
  23. break;
  24. } while (true);
  25. // We're done performing work. Time to clean up.
  26. isWorking = false;
  27. ReactCurrentDispatcher.current = null;
  28. resetContextDependences();
  29. resetHooks();
  30. // We completed the whole tree.
  31. const didCompleteRoot = true;
  32. stopWorkLoopTimer(interruptedBy, didCompleteRoot);// 删除 "(React Tree Reconciliation)"
  33. // Ready to commit.
  34. onComplete(root, rootWorkInProgress, expirationTime);
  35. }
  36. function onComplete(root, finishedWork, expirationTime) {
  37. root.pendingCommitExpirationTime = expirationTime;
  38. root.finishedWork = finishedWork;
  39. }

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是逐渐生成的。

ReactTreeReconciliation.gif
图参考于

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。

  1. function workLoop(isYieldy) {
  2. if (!isYieldy) {
  3. // Flush work without yielding
  4. while (nextUnitOfWork !== null) {
  5. nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  6. }
  7. } else {// Flush asynchronous work until there's a higher priority event
  8. while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
  9. nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  10. }
  11. }
  12. }

performUnitOfWork 用于完成 Unit Of Work,同时并返回next,主要方法是beginWork。

  1. function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  2. // The current, flushed, state of this fiber is the alternate.
  3. // Ideally nothing should rely on this, but relying on it here
  4. // means that we don't need an additional field on the work in
  5. // progress.
  6. const current = workInProgress.alternate;//此处 alternate
  7. // See if beginning this work spawns more work.
  8. startWorkTimer(workInProgress);
  9. let next;
  10. if (enableProfilerTimer) {
  11. if (workInProgress.mode & ProfileMode) {
  12. startProfilerTimer(workInProgress);
  13. }
  14. next = beginWork(current, workInProgress, nextRenderExpirationTime);
  15. workInProgress.memoizedProps = workInProgress.pendingProps;
  16. if (workInProgress.mode & ProfileMode) {
  17. // Record the render duration assuming we didn't bailout (or error).
  18. stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
  19. }
  20. } else {
  21. next = beginWork(current, workInProgress, nextRenderExpirationTime);
  22. workInProgress.memoizedProps = workInProgress.pendingProps;
  23. }
  24. if (next === null) {
  25. // If this doesn't spawn new work, complete the current work.
  26. next = completeUnitOfWork(workInProgress);
  27. }
  28. ReactCurrentOwner.current = null;
  29. return next;
  30. }

传入beginWork中的 current 是 workInProgress.alternate,workInProgress是创建的fiber。