学习资料:https://react.iamkasong.com/process/reconciler.html#%E9%80%92-%E9%98%B6%E6%AE%B5

render阶段过程:采用深度优先遍历的方式,依次执行beginWork和completeWork

第一次进入beginWork,存在current,tag为3
image.png
3表示是根节点
image.png
下一次执行,进入function App的beginWork,对于双缓存机制,再一次进入current为null,
image.png
存在workInProgress
image.png
测试demo
image.png
再执行一次,下一个进入beginWork的是div
image.png
再执行一次,下一个进入beginWork的是header
image.png
再执行一次,下一个进入beginWork的是img
image.png
由于img没有子节点,执行completeWork
image.png
img的completeWork执行完后,会执行img的兄弟节点P的BeginWork
image.png
此时p有3个子节点,Edit<code>src/App.js</code>and save to reload,下一次执行,进入p的子节点Edit
image.png
由于Edit没有子节点,会进入Edit的completeWork
image.png
Edit的completeWork执行完之后,会进入Edit的兄弟节点的beginWork
image.png
在执行一次,进入了code的completeWork,因为React对只有一个文本节点的子节点做了优化,这个子节点不会产生自己的Fiber节点。因此下一次执行了code的completeWork。
image.png
接下来,code寻找自己的兄弟节点文本节点and save to reload的beginWork,再继续执行文本节点的completeWork。
下一次执行,由于文本节点没有兄弟节点,会执行p的completeWork。
下一次执行,a节点的beginWork。
下一次执行,由于a节点的子节点是文本节点,执行completeWork。
下一次执行,由于a节点没有兄弟节点,执行header的completeWork。
下一次执行,由于header节点没有兄弟节点,执行#app div的completeWork。
下一次执行,由于#app div节点没有兄弟节点,执行方法App的completeWork。
image.png
再一次执行,App的根节点,即tag为3的根节点的completeWork。
image.png
在接下来,rander阶段完成,进入commit阶段,渲染页面。

BeginWork的作用 —传入当前Fiber节点,创建子Fiber节点,并将者两个Fiber节点连接起来。当遍历到叶子节点(即没有子节点的组件)时,就会进入”归”阶段。

  1. function beginWork(current$$1, workInProgress, renderExpirationTime) {
  2. var updateExpirationTime = workInProgress.expirationTime;
  3. ...
  4. // 根据不同的tag,进入不同的case
  5. workInProgress.expirationTime = NoWork;
  6. switch (workInProgress.tag) {
  7. case IndeterminateComponent:
  8. ...
  9. case LazyComponent:
  10. ...
  11. case FunctionComponent:
  12. ...
  13. case ClassComponent:
  14. ...
  15. case HostRoot:
  16. return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
  17. case HostComponent:
  18. return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
  19. case HostText:
  20. return updateHostText(current$$1, workInProgress);
  21. case SuspenseComponent:
  22. return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
  23. case HostPortal:
  24. return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
  25. case ForwardRef:
  26. ...
  27. }
  28. }

updateHostComponent

以div举例,div是HostComponent,进入updateHostComponent

  1. function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
  2. pushHostContext(workInProgress);
  3. if (current$$1 === null) {
  4. tryToClaimNextHydratableInstance(workInProgress);
  5. }
  6. //先赋值
  7. var type = workInProgress.type;
  8. var nextProps = workInProgress.pendingProps;
  9. var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
  10. var nextChildren = nextProps.children;
  11. //检查当前的fiber节点,是否只有一个唯一的文本子节点,比如a标签,
  12. //如果是唯一的文本子节点,react不会创建fiber节点(优化点)
  13. var isDirectTextChild = shouldSetTextContent(type, nextProps);
  14. if (isDirectTextChild) {
  15. nextChildren = null;
  16. } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
  17. workInProgress.effectTag |= ContentReset;
  18. }
  19. markRef(current$$1, workInProgress);
  20. // Check the host config to see if the children are offscreen/hidden.
  21. if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode && shouldDeprioritizeSubtree(type, nextProps)) {
  22. // Schedule this fiber to re-render at offscreen priority. Then bailout.
  23. workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
  24. return null;
  25. }
  26. //进入reconcileChildren方法
  27. reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
  28. return workInProgress.child;
  29. }

reconcileChildren

再执行reconcileChildren之前,当前的workInProgress.child是null
image.png

  1. function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
  2. if (current$$1 === null) {
  3. /*如果这是一个尚未渲染的全新组件,我们
  4. 不会通过应用最小的副作用来更新其子集。相反
  5. 在渲染之前,我们会将它们全部添加到子级中。这意味着
  6. 我们可以通过不跟踪副作用来优化这种对账传递。*/
  7. workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
  8. } else {
  9. /* 如果当前子项与正在进行的工作相同,则意味着
  10. 我们还没有开始对这些孩子进行任何研究。因此,我们使用
  11. 克隆算法以创建所有当前子级的副本。
  12. 如果我们已经有任何进展,那么在这一点上是无效的,所以
  13. 让我们把它扔掉。*/
  14. workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
  15. }
  16. }

mountChildFibers与mountChildFiber都是ChildReconciler方法根据不同的Boolean值处理的。

  1. export const reconcileChildFibers = ChildReconciler(true);
  2. export const mountChildFibers = ChildReconciler(false);

查看Placement的值,
ReactFiberFlags.js保存了所有的副作用,render阶段不会进行具体dom操作,具体dom操作是在commit阶段执行的,render阶段为需要执行操作的fiber节点打上标记。

  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow
  8. */
  9. import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
  10. export type Flags = number;
  11. // Don't change these two values. They're used by React Dev Tools.
  12. export const NoFlags = /* */ 0b00000000000000000000000000;
  13. export const PerformedWork = /* */ 0b00000000000000000000000001;
  14. // You can change the rest (and add more).
  15. export const Placement = /* */ 0b00000000000000000000000010;
  16. export const Update = /* */ 0b00000000000000000000000100;
  17. export const Deletion = /* */ 0b00000000000000000000001000;
  18. export const ChildDeletion = /* */ 0b00000000000000000000010000;
  19. export const ContentReset = /* */ 0b00000000000000000000100000;
  20. export const Callback = /* */ 0b00000000000000000001000000;
  21. export const DidCapture = /* */ 0b00000000000000000010000000;
  22. export const ForceClientRender = /* */ 0b00000000000000000100000000;
  23. export const Ref = /* */ 0b00000000000000001000000000;
  24. export const Snapshot = /* */ 0b00000000000000010000000000;
  25. export const Passive = /* */ 0b00000000000000100000000000;
  26. export const Hydrating = /* */ 0b00000000000001000000000000;
  27. export const Visibility = /* */ 0b00000000000010000000000000;
  28. export const StoreConsistency = /* */ 0b00000000000100000000000000;
  29. export const LifecycleEffectMask =
  30. Passive | Update | Callback | Ref | Snapshot | StoreConsistency;
  31. // Union of all commit flags (flags with the lifetime of a particular commit)
  32. export const HostEffectMask = /* */ 0b00000000000111111111111111;
  33. // These are not really side effects, but we still reuse this field.
  34. export const Incomplete = /* */ 0b00000000001000000000000000;
  35. export const ShouldCapture = /* */ 0b00000000010000000000000000;
  36. export const ForceUpdateForLegacySuspense = /* */ 0b00000000100000000000000000;
  37. export const DidPropagateContext = /* */ 0b00000001000000000000000000;
  38. export const NeedsPropagation = /* */ 0b00000010000000000000000000;
  39. export const Forked = /* */ 0b00000100000000000000000000;
  40. // Static tags describe aspects of a fiber that are not specific to a render,
  41. // e.g. a fiber uses a passive effect (even if there are no updates on this particular render).
  42. // This enables us to defer more work in the unmount case,
  43. // since we can defer traversing the tree during layout to look for Passive effects,
  44. // and instead rely on the static flag as a signal that there may be cleanup work.
  45. export const RefStatic = /* */ 0b00001000000000000000000000;
  46. export const LayoutStatic = /* */ 0b00010000000000000000000000;
  47. export const PassiveStatic = /* */ 0b00100000000000000000000000;
  48. // These flags allow us to traverse to fibers that have effects on mount
  49. // without traversing the entire tree after every commit for
  50. // double invoking
  51. export const MountLayoutDev = /* */ 0b01000000000000000000000000;
  52. export const MountPassiveDev = /* */ 0b10000000000000000000000000;
  53. // Groups of flags that are used in the commit phase to skip over trees that
  54. // don't contain effects, by checking subtreeFlags.
  55. export const BeforeMutationMask =
  56. // TODO: Remove Update flag from before mutation phase by re-landing Visibility
  57. // flag logic (see #20043)
  58. Update |
  59. Snapshot |
  60. (enableCreateEventHandleAPI
  61. ? // createEventHandle needs to visit deleted and hidden trees to
  62. // fire beforeblur
  63. // TODO: Only need to visit Deletions during BeforeMutation phase if an
  64. // element is focused.
  65. ChildDeletion | Visibility
  66. : 0);
  67. export const MutationMask =
  68. Placement |
  69. Update |
  70. ChildDeletion |
  71. ContentReset |
  72. Ref |
  73. Hydrating |
  74. Visibility;
  75. export const LayoutMask = Update | Callback | Ref | Visibility;
  76. // TODO: Split into PassiveMountMask and PassiveUnmountMask
  77. export const PassiveMask = Passive | ChildDeletion;
  78. // Union of tags that don't get reset on clones.
  79. // This allows certain concepts to persist without recalculating them,
  80. // e.g. whether a subtree contains passive effects or portals.
  81. export const StaticMask = LayoutStatic | PassiveStatic | RefStatic;

image.png

reconcileChildFibers

进入reconcileChildFibers,

  1. function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
  2. var isUnkeyedTopLevelFragment =
  3. typeof newChild === "object" && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null
  4. if (isUnkeyedTopLevelFragment) {
  5. newChild = newChild.props.children
  6. }
  7. // 判断child类型,对不同类型,进入不同处理逻辑
  8. var isObject = typeof newChild === "object" && newChild !== null
  9. if (isObject) {
  10. switch (newChild.$$typeof) {
  11. //如果值是REACT_ELEMENT_TYPE,被当作一个单一的react_elment来处理
  12. case REACT_ELEMENT_TYPE:
  13. return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime))
  14. case REACT_PORTAL_TYPE:
  15. return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime))
  16. }
  17. }
  18. //如果当前的newChild是string或者number类型,会当作一个文本节点处理
  19. if (typeof newChild === "string" || typeof newChild === "number") {
  20. return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, "" + newChild, expirationTime))
  21. }
  22. //如果当前的newChild是数组,会当作一个数组处理。数组情况,例如header中有img,p,a三个子节点,即会被当作数组处理。
  23. if (isArray(newChild)) {
  24. return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime)
  25. }
  26. ...
  27. }

reconcileSingleElement

进入reconcileSingleElement,
image.png

  1. function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
  2. var key = element.key
  3. var child = currentFirstChild
  4. while (child !== null) {
  5. // TODO: If key === null and child.key === null, then this only applies to
  6. // the first item in the list.
  7. if (child.key === key) {
  8. if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type) {
  9. deleteRemainingChildren(returnFiber, child.sibling)
  10. var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime)
  11. existing.ref = coerceRef(returnFiber, child, element)
  12. existing.return = returnFiber
  13. {
  14. existing._debugSource = element._source
  15. existing._debugOwner = element._owner
  16. }
  17. return existing
  18. } else {
  19. deleteRemainingChildren(returnFiber, child)
  20. break
  21. }
  22. } else {
  23. deleteChild(returnFiber, child)
  24. }
  25. child = child.sibling
  26. }
  27. if (element.type === REACT_FRAGMENT_TYPE) {
  28. var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key)
  29. created.return = returnFiber
  30. return created
  31. } else {
  32. //最终进入这里
  33. var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime)
  34. _created4.ref = coerceRef(returnFiber, currentFirstChild, element)
  35. _created4.return = returnFiber
  36. return _created4
  37. }
  38. }

createFiberFromElement

进入createFiberFromElement

  1. export function createFiberFromElement(
  2. element: ReactElement,
  3. mode: TypeOfMode,
  4. lanes: Lanes,
  5. ): Fiber {
  6. let owner = null;
  7. ...
  8. const type = element.type;
  9. const key = element.key;
  10. const pendingProps = element.props;
  11. const fiber = createFiberFromTypeAndProps(
  12. type,
  13. key,
  14. pendingProps,
  15. owner,
  16. mode,
  17. lanes,
  18. );
  19. ...
  20. return fiber;
  21. }

createFiberFromTypeAndProps

进入createFiberFromTypeAndProps
image.png

createFiberFromTypeAndProps

判断type类型,当前type类型为string,于是fiberTag = HostComponent;

  1. export function createFiberFromTypeAndProps(
  2. type: any, // React$ElementType
  3. key: null | string,
  4. pendingProps: any,
  5. owner: null | Fiber,
  6. mode: TypeOfMode,
  7. lanes: Lanes,
  8. ): Fiber {
  9. let fiberTag = IndeterminateComponent;
  10. //判断type类型
  11. let resolvedType = type;
  12. if (typeof type === 'function') {
  13. if (shouldConstruct(type)) {
  14. fiberTag = ClassComponent;
  15. }
  16. } else if (typeof type === 'string') {
  17. fiberTag = HostComponent;
  18. } else {
  19. ...
  20. }
  21. const fiber = createFiber(fiberTag, pendingProps, key, mode);
  22. fiber.elementType = type;
  23. fiber.type = resolvedType;
  24. fiber.lanes = lanes;
  25. return fiber;
  26. }

createFiber

进入createFiber,创建对应的fiber节点

  1. const createFiber = function(
  2. tag: WorkTag,
  3. pendingProps: mixed,
  4. key: null | string,
  5. mode: TypeOfMode,
  6. ): Fiber {
  7. //创建Fiber节点
  8. return new FiberNode(tag, pendingProps, key, mode);
  9. };

FiberNode

进入FiberNode,有很多的属性

  1. function FiberNode(
  2. tag: WorkTag,
  3. pendingProps: mixed,
  4. key: null | string,
  5. mode: TypeOfMode,
  6. ) {
  7. // Instance
  8. this.tag = tag;
  9. this.key = key;
  10. this.elementType = null;
  11. this.type = null;
  12. this.stateNode = null;
  13. // Fiber
  14. this.return = null;
  15. this.child = null;
  16. this.sibling = null;
  17. this.index = 0;
  18. this.ref = null;
  19. this.pendingProps = pendingProps;
  20. this.memoizedProps = null;
  21. this.updateQueue = null;
  22. this.memoizedState = null;
  23. this.dependencies = null;
  24. this.mode = mode;
  25. // Effects
  26. this.flags = NoFlags;
  27. this.subtreeFlags = NoFlags;
  28. this.deletions = null;
  29. this.lanes = NoLanes;
  30. this.childLanes = NoLanes;
  31. this.alternate = null;
  32. if (enableProfilerTimer) {
  33. this.actualDuration = Number.NaN;
  34. this.actualStartTime = Number.NaN;
  35. this.selfBaseDuration = Number.NaN;
  36. this.treeBaseDuration = Number.NaN;
  37. this.actualDuration = 0;
  38. this.actualStartTime = -1;
  39. this.selfBaseDuration = 0;
  40. this.treeBaseDuration = 0;
  41. }
  42. ...
  43. }

总结

image.png
当某一个Fiber进入beginWork时,他最终的目的是为了创建当前Fiber节点的第一个子Fiber节点
首先,进入UpdateHostComponent,判断当前Fiber节点的类型,进入不同update的逻辑
在reconcileChildren逻辑中,判断workInProgressFiber是否存在对应的currentFiber来决定是否标记effectTag
reconcileChildFibers:根据child节点的类型执行创建操作
最终进入createFiber,创建一个Fiber树。
当进入reconcileChildrenArray,该函数创建当前Fiber节点的子节点
image.png
此时,returnFiber指的是header,即创建img节点,而不是img,p,a子Fiber节点
image.png