performWorkOnRoot 中的 renderRoot 生成 React Tree Reconciliation,completeRoot 则是 commit 阶段。
例如,同步更新代码如下:

  1. function performWorkOnRoot(
  2. root: FiberRoot,
  3. expirationTime: ExpirationTime,
  4. isYieldy: boolean,
  5. ) {
  6. //...
  7. renderRoot(root, isYieldy);
  8. finishedWork = root.finishedWork;
  9. if (finishedWork !== null) {
  10. // We've completed the root. Commit it.
  11. completeRoot(root, finishedWork, expirationTime);
  12. }
  13. //...
  14. }

renderRoot之后,就开始completeRoot。此时的执行栈为:
image.png
commitRoot进入提交阶段。在completeRoot中可以阻止 commit 提交!

  1. function completeRoot(
  2. root: FiberRoot,finishedWork: Fiber, expirationTime: ExpirationTime
  3. ): void {
  4. const firstBatch = root.firstBatch;
  5. if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
  6. if (completedBatches === null) {
  7. completedBatches = [firstBatch];
  8. } else {
  9. completedBatches.push(firstBatch);
  10. }
  11. if (firstBatch._defer) {// 阻止提交
  12. root.finishedWork = finishedWork;
  13. root.expirationTime = NoWork;
  14. return;
  15. }
  16. }
  17. root.finishedWork = null;
  18. if (root === lastCommittedRootDuringThisBatch) {//嵌套循环计数
  19. nestedUpdateCount++;
  20. } else {
  21. lastCommittedRootDuringThisBatch = root;
  22. nestedUpdateCount = 0;
  23. }
  24. commitRoot(root, finishedWork);
  25. }

commitRoot

commitRoot分为commitBeforeMutationLifecycles 、commitAllHostEffects 、commitAllLifeCycles 三个阶段。

  1. function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
  2. isWorking = true;
  3. isCommitting = true;
  4. startCommitTimer();
  5. const committedExpirationTime = root.pendingCommitExpirationTime;
  6. root.pendingCommitExpirationTime = NoWork;
  7. const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
  8. const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
  9. const earliestRemainingTimeBeforeCommit =
  10. childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit
  11. ? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit;
  12. markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
  13. let prevInteractions: Set<Interaction> = (null: any);
  14. if (enableSchedulerTracing) {
  15. prevInteractions = __interactionsRef.current;
  16. __interactionsRef.current = root.memoizedInteractions;
  17. }
  18. ReactCurrentOwner.current = null;
  19. let firstEffect;
  20. if (finishedWork.effectTag > PerformedWork) {
  21. if (finishedWork.lastEffect !== null) {
  22. finishedWork.lastEffect.nextEffect = finishedWork;
  23. firstEffect = finishedWork.firstEffect;
  24. } else {
  25. firstEffect = finishedWork;
  26. }
  27. } else {
  28. firstEffect = finishedWork.firstEffect;
  29. }
  30. prepareForCommit(root.containerInfo);
  31. // Invoke instances of getSnapshotBeforeUpdate before mutation.
  32. nextEffect = firstEffect;
  33. startCommitSnapshotEffectsTimer();
  34. while (nextEffect !== null) {
  35. let didError = false;
  36. let error;
  37. try {
  38. commitBeforeMutationLifecycles();
  39. } catch (e) {
  40. didError = true;
  41. error = e;
  42. }
  43. if (didError) {
  44. captureCommitPhaseError(nextEffect, error);
  45. if (nextEffect !== null) {// Clean-up
  46. nextEffect = nextEffect.nextEffect;
  47. }
  48. }
  49. }
  50. stopCommitSnapshotEffectsTimer();
  51. if (enableProfilerTimer) {
  52. recordCommitTime();
  53. }
  54. nextEffect = firstEffect;
  55. startCommitHostEffectsTimer();
  56. while (nextEffect !== null) {
  57. let didError = false;
  58. let error;
  59. try {
  60. commitAllHostEffects();
  61. } catch (e) {
  62. didError = true;
  63. error = e;
  64. }
  65. if (didError) {
  66. captureCommitPhaseError(nextEffect, error);
  67. if (nextEffect !== null) {// Clean-up
  68. nextEffect = nextEffect.nextEffect;
  69. }
  70. }
  71. }
  72. stopCommitHostEffectsTimer();
  73. resetAfterCommit(root.containerInfo);
  74. root.current = finishedWork;
  75. nextEffect = firstEffect;
  76. startCommitLifeCyclesTimer();
  77. while (nextEffect !== null) {
  78. let didError = false;
  79. let error;
  80. try {
  81. commitAllLifeCycles(root, committedExpirationTime);
  82. } catch (e) {
  83. didError = true;
  84. error = e;
  85. }
  86. if (didError) {
  87. captureCommitPhaseError(nextEffect, error);
  88. if (nextEffect !== null) {
  89. nextEffect = nextEffect.nextEffect;
  90. }
  91. }
  92. }
  93. isCommitting = false;
  94. isWorking = false;
  95. stopCommitLifeCyclesTimer();
  96. stopCommitTimer();
  97. onCommitRoot(finishedWork.stateNode);
  98. const updateExpirationTimeAfterCommit = finishedWork.expirationTime;
  99. const childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
  100. const earliestRemainingTimeAfterCommit =
  101. childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit
  102. ? childExpirationTimeAfterCommit
  103. : updateExpirationTimeAfterCommit;
  104. if (earliestRemainingTimeAfterCommit === NoWork) {
  105. legacyErrorBoundariesThatAlreadyFailed = null;
  106. }
  107. onCommit(root, earliestRemainingTimeAfterCommit);//清空值
  108. //....
  109. }

执行这三个阶段使用的都是effect链,只做对有此效果的组件进行更新。

commitBeforeMutationLifecycles

commitBeforeMutationLifecycles 执行在mutation 之前的getSnapshotBeforeUpdate 生命周期方法;

  1. //packages\react-reconciler\src\ReactFiberScheduler.js
  2. function commitBeforeMutationLifecycles() {//结尾是 Lifecycles
  3. while (nextEffect !== null) {
  4. const effectTag = nextEffect.effectTag;
  5. if (effectTag & Snapshot) {//检验效果
  6. recordEffect();
  7. const current = nextEffect.alternate;
  8. commitBeforeMutationLifeCycles(current, nextEffect);// 结尾是Lifecycles
  9. }
  10. nextEffect = nextEffect.nextEffect;
  11. }
  12. }
  13. //名字相同,大小写不同
  14. function commitBeforeMutationLifeCycles(current: Fiber | null,finishedWork: Fiber): void {
  15. switch (finishedWork.tag) {
  16. case FunctionComponent:
  17. case ForwardRef:
  18. case SimpleMemoComponent: {
  19. commitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork);
  20. return;
  21. }
  22. case ClassComponent: {
  23. if (finishedWork.effectTag & Snapshot) {
  24. if (current !== null) {
  25. const prevProps = current.memoizedProps;
  26. const prevState = current.memoizedState;
  27. startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
  28. const instance = finishedWork.stateNode;
  29. const snapshot = instance.getSnapshotBeforeUpdate(
  30. finishedWork.elementType === finishedWork.type
  31. ? prevProps : resolveDefaultProps(finishedWork.type, prevProps),
  32. prevState,
  33. );
  34. instance.__reactInternalSnapshotBeforeUpdate = snapshot;
  35. stopPhaseTimer();
  36. }
  37. }
  38. return;
  39. }
  40. case HostRoot:
  41. case HostComponent:
  42. case HostText:
  43. case HostPortal:
  44. case IncompleteClassComponent:
  45. // Nothing to do for these component types
  46. return;
  47. default: {
  48. invariant(
  49. false,
  50. 'This unit of work tag should not have side-effects. This error is ' +
  51. 'likely caused by a bug in React. Please file an issue.',
  52. );
  53. }
  54. }
  55. }

commitAllHostEffects

commitAllHostEffects 中将 staticNode 添加到容器中;

  1. function commitAllHostEffects() {
  2. while (nextEffect !== null) {
  3. recordEffect();
  4. const effectTag = nextEffect.effectTag;
  5. if (effectTag & ContentReset) {
  6. commitResetTextContent(nextEffect);//清空文本
  7. }
  8. if (effectTag & Ref) {
  9. const current = nextEffect.alternate;
  10. if (current !== null) {
  11. commitDetachRef(current);
  12. }
  13. }
  14. let primaryEffectTag = effectTag & (Placement | Update | Deletion);
  15. switch (primaryEffectTag) {
  16. case Placement: {
  17. commitPlacement(nextEffect);//挂载
  18. nextEffect.effectTag &= ~Placement;
  19. break;
  20. }
  21. case PlacementAndUpdate: {
  22. commitPlacement(nextEffect);
  23. nextEffect.effectTag &= ~Placement;
  24. const current = nextEffect.alternate;
  25. commitWork(current, nextEffect);
  26. break;
  27. }
  28. case Update: {
  29. const current = nextEffect.alternate;
  30. commitWork(current, nextEffect);
  31. break;
  32. }
  33. case Deletion: {
  34. commitDeletion(nextEffect);
  35. break;
  36. }
  37. }
  38. nextEffect = nextEffect.nextEffect;
  39. }
  40. }
  41. function commitDetachRef(current: Fiber) {
  42. const currentRef = current.ref;
  43. if (currentRef !== null) {
  44. if (typeof currentRef === 'function') {
  45. currentRef(null);
  46. } else {
  47. currentRef.current = null;
  48. }
  49. }
  50. }

commitPlacement

在 commitPlacement 中查找父元素,并将子元素追加到容器中。

  1. function commitPlacement(finishedWork: Fiber): void {
  2. if (!supportsMutation) { return; }
  3. //递归地将所有主机节点插入父节点
  4. const parentFiber = getHostParentFiber(finishedWork);
  5. // 注意:这两个变量*必须*总是一起更新
  6. let parent;
  7. let isContainer;
  8. switch (parentFiber.tag) {
  9. case HostComponent:
  10. parent = parentFiber.stateNode;
  11. isContainer = false;
  12. break;
  13. case HostRoot:
  14. parent = parentFiber.stateNode.containerInfo;
  15. isContainer = true;
  16. break;
  17. case HostPortal:
  18. parent = parentFiber.stateNode.containerInfo;
  19. isContainer = true;
  20. break;
  21. default:
  22. invariant(false, 'Invalid host parent fiber. This error is likely caused by a bug ' +
  23. 'in React. Please file an issue.' );
  24. }
  25. if (parentFiber.effectTag & ContentReset) {
  26. // 在执行任何插入之前重置父类的文本内容
  27. resetTextContent(parent);
  28. // 从效果标签中清除ContentReset
  29. parentFiber.effectTag &= ~ContentReset;
  30. }
  31. const before = getHostSibling(finishedWork);
  32. let node: Fiber = finishedWork;
  33. while (true) {
  34. if (node.tag === HostComponent || node.tag === HostText) {
  35. if (before) {
  36. if (isContainer) {
  37. insertInContainerBefore(parent, node.stateNode, before);
  38. } else {
  39. insertBefore(parent, node.stateNode, before);
  40. }
  41. } else {
  42. if (isContainer) {// 将子节点追加到容器中
  43. appendChildToContainer(parent, node.stateNode);
  44. } else {
  45. appendChild(parent, node.stateNode);
  46. }
  47. }
  48. } else if (node.tag === HostPortal) {
  49. } else if (node.child !== null) {
  50. node.child.return = node;
  51. node = node.child;
  52. continue;
  53. }
  54. if (node === finishedWork) { return; }
  55. while (node.sibling === null) {
  56. if (node.return === null || node.return === finishedWork) { return; }
  57. node = node.return;
  58. }
  59. node.sibling.return = node.return;
  60. node = node.sibling;
  61. }
  62. }

在commitPlacement 中实现第一次渲染:浏览器中可见到dom树。

commitWork

最主要更新的是HostComponent、HostText、SuspenseComponent。HostComponent 更新时会参照UpdatePayload(finishedWork.updateQueue) 来进行更新。

  1. function commitWork(current: Fiber | null, finishedWork: Fiber): void {
  2. if (!supportsMutation) {
  3. switch (finishedWork.tag) {
  4. case FunctionComponent:
  5. case ForwardRef:
  6. case MemoComponent:
  7. case SimpleMemoComponent: {
  8. commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
  9. return;
  10. }
  11. }
  12. commitContainer(finishedWork);
  13. return;
  14. }
  15. switch (finishedWork.tag) {
  16. case FunctionComponent:
  17. case ForwardRef:
  18. case MemoComponent:
  19. case SimpleMemoComponent: {
  20. //注意:我们目前从不使用MountMutation,但是useLayout使用UnmountMutation.
  21. commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
  22. return;
  23. }
  24. case ClassComponent: {
  25. return; }
  26. case HostComponent: {
  27. const instance: Instance = finishedWork.stateNode;
  28. if (instance != null) {// 提前完成准备好的工作.
  29. const newProps = finishedWork.memoizedProps;
  30. const oldProps = current !== null ? current.memoizedProps : newProps;
  31. const type = finishedWork.type;
  32. const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
  33. finishedWork.updateQueue = null;
  34. if (updatePayload !== null) {
  35. commitUpdate(instance,updatePayload,type,oldProps,newProps,finishedWork);
  36. }
  37. }
  38. return;
  39. }
  40. case HostText: {
  41. const textInstance: TextInstance = finishedWork.stateNode;
  42. const newText: string = finishedWork.memoizedProps;
  43. const oldText: string = current !== null ? current.memoizedProps : newText;
  44. commitTextUpdate(textInstance, oldText, newText);
  45. return;
  46. }
  47. case HostRoot: { return; }
  48. case Profiler: { return; }
  49. case SuspenseComponent: {
  50. let newState: SuspenseState | null = finishedWork.memoizedState;
  51. let newDidTimeout;
  52. let primaryChildParent = finishedWork;
  53. if (newState === null) {
  54. newDidTimeout = false;
  55. } else {
  56. newDidTimeout = true;
  57. primaryChildParent = finishedWork.child;
  58. if (newState.timedOutAt === NoWork) {
  59. newState.timedOutAt = requestCurrentTime();
  60. }
  61. }
  62. if (primaryChildParent !== null) {
  63. hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
  64. }
  65. // 如果这个边界刚好超时,那么它将有一组thenable。
  66. // 对于每个thenable,附加一个侦听器,以便当它解析时,React尝试以主(预超时)状态重新呈现边界。
  67. const thenables: Set<Thenable> | null = (finishedWork.updateQueue: any);
  68. if (thenables !== null) {
  69. finishedWork.updateQueue = null;
  70. let retryCache = finishedWork.stateNode;
  71. if (retryCache === null) {
  72. retryCache = finishedWork.stateNode = new PossiblyWeakSet();
  73. }
  74. thenables.forEach(thenable => {
  75. // Memoize using the boundary fiber to prevent redundant listeners.
  76. let retry = retryTimedOutBoundary.bind(null, finishedWork, thenable);
  77. if (enableSchedulerTracing) {
  78. retry = Schedule_tracing_wrap(retry);
  79. }
  80. if (!retryCache.has(thenable)) {
  81. retryCache.add(thenable);
  82. thenable.then(retry, retry);
  83. }
  84. });
  85. }
  86. return;
  87. }
  88. case IncompleteClassComponent: {
  89. return;
  90. }
  91. default: {
  92. invariant(
  93. false,
  94. 'This unit of work tag should not have side-effects. This error is ' +
  95. 'likely caused by a bug in React. Please file an issue.',
  96. );
  97. }
  98. }
  99. }

react会保留dom对象的引用,更新HostComponent、HostText 时直接使用对应的引用,完成具体 dom 或文本的更新。commitAllHostEffects 在一个大的循环中进行的,更新所有的元素。

commitAllLifeCycles

挂载或更新完成后,进入到commitAllLifeCycles 阶段。commitAllLifeCycles 执行componentDidMount或componentDidUpdate。

  1. function commitAllLifeCycles(finishedRoot: FiberRoot,committedExpirationTime: ExpirationTime) {
  2. while (nextEffect !== null) {
  3. const effectTag = nextEffect.effectTag;
  4. if (effectTag & (Update | Callback)) {
  5. recordEffect();
  6. const current = nextEffect.alternate;
  7. commitLifeCycles(finishedRoot,current,nextEffect,committedExpirationTime);
  8. }
  9. if (effectTag & Ref) {
  10. recordEffect();
  11. commitAttachRef(nextEffect);
  12. }
  13. if (enableHooks && effectTag & Passive) {
  14. rootWithPendingPassiveEffects = finishedRoot;
  15. }
  16. nextEffect = nextEffect.nextEffect;
  17. }
  18. }

commitLifeCycles

执行相应组件的生命周期。

  1. function commitLifeCycles(finishedRoot: FiberRoot, current: Fiber | null,finishedWork: Fiber,
  2. committedExpirationTime: ExpirationTime,
  3. ): void {
  4. switch (finishedWork.tag) {
  5. case FunctionComponent:
  6. case ForwardRef:
  7. case SimpleMemoComponent: {
  8. commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
  9. break;
  10. }
  11. case ClassComponent: {
  12. const instance = finishedWork.stateNode;
  13. if (finishedWork.effectTag & Update) {
  14. if (current === null) {
  15. startPhaseTimer(finishedWork, 'componentDidMount');
  16. instance.componentDidMount();
  17. stopPhaseTimer();
  18. } else {
  19. const prevProps =
  20. finishedWork.elementType === finishedWork.type
  21. ? current.memoizedProps
  22. : resolveDefaultProps(finishedWork.type, current.memoizedProps);
  23. const prevState = current.memoizedState;
  24. startPhaseTimer(finishedWork, 'componentDidUpdate');
  25. instance.componentDidUpdate(prevProps,prevState,
  26. instance.__reactInternalSnapshotBeforeUpdate);
  27. stopPhaseTimer();
  28. }
  29. }
  30. const updateQueue = finishedWork.updateQueue;
  31. if (updateQueue !== null) {
  32. commitUpdateQueue(finishedWork,updateQueue,instance,committedExpirationTime);
  33. }
  34. return;
  35. }
  36. case HostRoot: {
  37. const updateQueue = finishedWork.updateQueue;
  38. if (updateQueue !== null) {
  39. let instance = null;
  40. if (finishedWork.child !== null) {
  41. switch (finishedWork.child.tag) {
  42. case HostComponent:
  43. instance = getPublicInstance(finishedWork.child.stateNode);
  44. break;
  45. case ClassComponent:
  46. instance = finishedWork.child.stateNode;
  47. break;
  48. }
  49. }
  50. commitUpdateQueue(finishedWork,updateQueue,instance,committedExpirationTime);
  51. }
  52. return;
  53. }
  54. case HostComponent: {
  55. const instance: Instance = finishedWork.stateNode;
  56. if (current === null && finishedWork.effectTag & Update) {
  57. const type = finishedWork.type;
  58. const props = finishedWork.memoizedProps;
  59. commitMount(instance, type, props, finishedWork);
  60. }
  61. return;
  62. }
  63. case HostText: {
  64. return;
  65. }
  66. case HostPortal: {
  67. return;
  68. }
  69. case Profiler: {
  70. if (enableProfilerTimer) {
  71. const onRender = finishedWork.memoizedProps.onRender;
  72. if (enableSchedulerTracing) {
  73. onRender(finishedWork.memoizedProps.id,
  74. current === null ? 'mount' : 'update',
  75. finishedWork.actualDuration,
  76. finishedWork.treeBaseDuration,
  77. finishedWork.actualStartTime,
  78. getCommitTime(),
  79. finishedRoot.memoizedInteractions,
  80. );
  81. } else {
  82. onRender(
  83. finishedWork.memoizedProps.id,
  84. current === null ? 'mount' : 'update',
  85. finishedWork.actualDuration,
  86. finishedWork.treeBaseDuration,
  87. finishedWork.actualStartTime,
  88. getCommitTime(),
  89. );
  90. }
  91. }
  92. return;
  93. }
  94. case SuspenseComponent:
  95. break;
  96. case IncompleteClassComponent:
  97. break;
  98. default: {
  99. invariant(
  100. false,
  101. 'This unit of work tag should not have side-effects. This error is ' +
  102. 'likely caused by a bug in React. Please file an issue.',
  103. );
  104. }
  105. }
  106. }

我们常用的是ClassComponent中componentDidMount、componentDidUpdate 生命周期,如果在组件生命周期中调用setState,则会触发二次渲染。至此组件挂载和生命周期基本完成。