在beginWork中可以看出大多数类型都会调用 reconcileChildren生成Children,并调用reconcileChildFibers生成child fibers并返回workInProgress.child 或 nextUnitOfWork,详细代码请看官网。beginWork是在workLoop中,在此构成的循环栈为workLoop -> performUnitOfWork -> beginWork -> updateWorkTags ->reconcileChildren -> reconcileChildFibers -> workLoop,其中workTags是中work的类型标签。

  1. //packages\react-reconciler\src\ReactChildFiber.js
  2. export const reconcileChildFibers = ChildReconciler(true);//
  3. export const mountChildFibers = ChildReconciler(false);
  4. export function reconcileChildren(
  5. current: Fiber | null,
  6. workInProgress: Fiber,
  7. nextChildren: any,
  8. renderExpirationTime: ExpirationTime,
  9. ) {
  10. if (current === null) {
  11. workInProgress.child = mountChildFibers(workInProgress, null,
  12. nextChildren, renderExpirationTime);
  13. } else {
  14. // init App 走此处,current 为
  15. workInProgress.child = reconcileChildFibers(workInProgress, current.child,
  16. nextChildren,renderExpirationTime);
  17. }
  18. }

reconcileChildren 中的 mountChildFibers 和 reconcileChildFibers 调用的都是 ChildReconciler。ChildReconciler是一个小型的Reconciler。ChildReconciler的代码如下:

  1. function ChildReconciler(shouldTrackSideEffects) {//是否追踪Side-Effects
  2. //....
  3. function reconcileChildFibers(
  4. returnFiber: Fiber,
  5. currentFirstChild: Fiber | null,
  6. newChild: any,
  7. expirationTime: ExpirationTime,
  8. ): Fiber | null {
  9. // 这个函数不是递归的。如果顶层项目是一个数组,我们将它视为一组子元素,而不是一个片段.
  10. // 另一方面,嵌套数组将被视为片段节点. 递归在正常的流程中发生.
  11. const isUnkeyedTopLevelFragment =
  12. typeof newChild === 'object' &&
  13. newChild !== null &&
  14. newChild.type === REACT_FRAGMENT_TYPE &&
  15. newChild.key === null;
  16. if (isUnkeyedTopLevelFragment) {
  17. newChild = newChild.props.children;
  18. }
  19. // Handle object types
  20. const isObject = typeof newChild === 'object' && newChild !== null;
  21. if (isObject) {
  22. switch (newChild.$$typeof) {
  23. case REACT_ELEMENT_TYPE:
  24. return placeSingleChild(
  25. reconcileSingleElement(returnFiber,currentFirstChild,newChild,expirationTime)
  26. );
  27. case REACT_PORTAL_TYPE:
  28. return placeSingleChild(
  29. reconcileSinglePortal(returnFiber,currentFirstChild,newChild,expirationTime)
  30. );
  31. }
  32. }
  33. if (typeof newChild === 'string' || typeof newChild === 'number') {
  34. return placeSingleChild(
  35. reconcileSingleTextNode( returnFiber,currentFirstChild,'' + newChild,expirationTime)
  36. );
  37. }
  38. if (isArray(newChild)) {
  39. return reconcileChildrenArray(returnFiber,currentFirstChild,newChild,expirationTime);
  40. }
  41. if (getIteratorFn(newChild)) {
  42. return reconcileChildrenIterator(returnFiber,currentFirstChild,newChild,expirationTime);
  43. }
  44. if (isObject) {
  45. throwOnInvalidObjectType(returnFiber, newChild);
  46. }
  47. if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
  48. //...
  49. }
  50. // Remaining cases are all treated as empty.
  51. return deleteRemainingChildren(returnFiber, currentFirstChild);
  52. }
  53. return reconcileChildFibers;
  54. }

mountChildFibers 和 reconcileChildFibers 的值都是 ChildReconciler 的返回值是reconcileChildFibers。ChildReconciler reconcileChildFibers 生成 workTags 类型的fiber,ChildReconciler reconcileChildFibers执行返回的fiber是workLoop中 nextUnitOfWork。看下执行代码:

  1. function workLoop(isYieldy) {
  2. while (nextUnitOfWork !== null) {
  3. nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  4. }
  5. }
  6. function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  7. const current = workInProgress.alternate;
  8. next = beginWork(current, workInProgress, nextRenderExpirationTime);
  9. return next;
  10. }
  11. function beginWork(current: Fiber | null, workInProgress: Fiber,
  12. renderExpirationTime: ExpirationTime,
  13. ): Fiber | null {
  14. return updateWorkTagsComponent(current, workInProgress, Component,
  15. resolvedProps, renderExpirationTime)
  16. }
  17. function updateWorkTagsComponent(current, workInProgress, Component,
  18. resolvedProps, renderExpirationTime){
  19. reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
  20. return workInProgress.child
  21. }

workLoop -> reconcileChildren 执行过程中有一些参数身份上的变化:
workInProgress.child 在 workLoop 中是 nextUnitOfWork;
nextUnitOfWork 在performUnitOfWork 是workInProgress;

以App Component 为例,代码如下:

  1. class App extends Component {
  2. render() {
  3. return (<div id='appId'>Hello World!</div>);
  4. }
  5. }
  6. ReactDOM.render(<App/>, document.getElementById('root'));

在 reconcileChildFibers 做断电,调用栈如下:
image.png
无论ReactDom.render渲染什么元素,第一个 nextUnitOfWork 是 FiberRoot(类型是HostRoot)。此时执行的代码如下:

  1. function performUnitOfWork(workInProgress) {
  2. var current$$1 = workInProgress.alternate;
  3. next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
  4. workInProgress.memoizedProps = workInProgress.pendingProps;
  5. //...
  6. }
  7. function beginWork(current$$1, workInProgress, renderExpirationTime) {
  8. case HostRoot:
  9. return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
  10. }
  11. function updateHostRoot(current$$1, workInProgress, renderExpirationTime){
  12. var updateQueue = workInProgress.updateQueue;
  13. var nextProps = workInProgress.pendingProps;
  14. var prevState = workInProgress.memoizedState;
  15. processUpdateQueue(workInProgress, updateQueue, nextProps, null, renderExpirationTime);
  16. var nextState = workInProgress.memoizedState;
  17. // being called "element".
  18. var nextChildren = nextState.element;//state中负载element(App ReactElement)
  19. var root = workInProgress.stateNode;
  20. if(...){ //省略 }else{
  21. reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
  22. resetHydrationState();
  23. }
  24. return workInProgress.child;
  25. }
  26. function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
  27. //...
  28. workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child,
  29. nextChildren, renderExpirationTime);
  30. //...
  31. }
  32. function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
  33. //....
  34. const isObject = typeof newChild === 'object' && newChild !== null;
  35. if (isObject) {
  36. switch (newChild.$$typeof) {
  37. case REACT_ELEMENT_TYPE:
  38. return placeSingleChild(
  39. reconcileSingleElement(returnFiber,currentFirstChild,newChild,expirationTime)
  40. );
  41. }
  42. }
  43. //...
  44. }

从performUnitOfWork 执行到reconcileChildFibers时,变量的名称也发生了变化:
workInProgress 到 reconcileChildFibers中则为 returnFiber;
workInProgress.alternate.child 到 reconcileChildFibers 则为 currentFirstChild,updateHostRoot 时值为null;
nextChildren 到 reconcileChildFibers 中则为newChild;
renderExpirationTime 到 reconcileChildFibers 中则为expirationTime,updateHostRoot时值为1073741823;

在updateHostRoot 中得到 nextChildren,此时nextChildren为:
reconcileChildren - 图2

reconcileType

在ChildReconciler 中 reconcileChildFibers 根据newChild 的类型,执行不同 reconcileType。常用的有三种类型,分别是REACT_ELEMENT_TYPE reconcileSingleElement、text的reconcileSingleTextNode、array的 reconcileChildrenArray。

reconcileSingleElement

由于reconcileChildFibers 的 typeof nextChildren 是’object’类型,同时newChild.$$typeof是 REACT_ELEMENT_TYPE 所以执行reconcileSingleElement。

  1. function reconcileSingleElement(
  2. returnFiber: Fiber,
  3. currentFirstChild: Fiber | null,
  4. element: ReactElement,
  5. expirationTime: ExpirationTime,
  6. ): Fiber {
  7. const key = element.key;
  8. let child = currentFirstChild;
  9. while (child !== null) {
  10. if (child.key === key) {
  11. if (child.tag === FragmentFragment ? element.type === REACT_FRAGMENT_TYPE
  12. : child.elementType === element.type
  13. ) {
  14. deleteRemainingChildren(returnFiber, child.sibling);//单元素删除sibling??
  15. const existing = useFiber(
  16. child,
  17. element.type === REACT_FRAGMENT_TYPE
  18. ? element.props.children
  19. : element.props,
  20. expirationTime,
  21. );
  22. existing.ref = coerceRef(returnFiber, child, element);
  23. existing.return = returnFiber;
  24. return existing;
  25. } else {
  26. deleteRemainingChildren(returnFiber, child);
  27. break;
  28. }
  29. } else {
  30. deleteChild(returnFiber, child);
  31. }
  32. child = child.sibling;
  33. }
  34. if (element.type === REACT_FRAGMENT_TYPE) {
  35. const created = createFiberFromFragment(
  36. element.props.children,returnFiber.mode, expirationTime, element.key
  37. );
  38. created.return = returnFiber;
  39. return created;
  40. } else {
  41. const created = createFiberFromElement(element, returnFiber.mode, expirationTime);
  42. created.ref = coerceRef(returnFiber, currentFirstChild, element);
  43. created.return = returnFiber;
  44. return created;
  45. }
  46. }

有一处代码写的比较有意思,多判断还可以这样写:

  1. if(child.tag === FragmentFragment ? element.type === REACT_FRAGMENT_TYPE
  2. : child.elementType === element.type)

此时currentFirstChild为null,Relement.type为EACT_ELEMENT_TYPE ,所以执行:

  1. const created = createFiberFromElement(element, returnFiber.mode, expirationTime);
  2. created.ref = coerceRef(returnFiber, currentFirstChild, element);
  3. created.return = returnFiber;
  4. return created;

此时就进入到创建fiber阶段。

fiber

ReactFiber有一个js文件,具体看官网。fiber其实是一个fiberNode。
可以通过Type 和 Props、reactElemennt、fragment、text等来创建,具体的创建方法有:

  • createHostRootFiber
  • createFiberFromTypeAndProps
  • createFiberFromElement
  • createFiberFromFragment
  • createFiberFromProfiler
  • createFiberFromMode
  • createFiberFromSuspense
  • createFiberFromText
  • createFiberFromHostInstanceForDeletion //元素被移除
  • createFiberFromPortal

fiber的结构基本相似,只是WorkTag不同。在App Component 中 使用的是createFiberFromElement.

createFiberFromElement

  1. export function createFiberFromElement(
  2. element: ReactElement, mode: TypeOfMode,expirationTime: ExpirationTime,
  3. ): Fiber {
  4. let owner = null;
  5. if (__DEV__) { owner = element._owner;}
  6. const type = element.type;
  7. const key = element.key;
  8. const pendingProps = element.props;
  9. const fiber = createFiberFromTypeAndProps(type,key,pendingProps,owner,mode,expirationTime);
  10. if (__DEV__) {
  11. fiber._debugSource = element._source;
  12. fiber._debugOwner = element._owner;
  13. }
  14. return fiber;
  15. }

element是react element,pendingProps 值为 element.props。

createFiberFromTypeAndProps

  1. export function createFiberFromTypeAndProps(
  2. type: any, // React$ElementType
  3. key: null | string,
  4. pendingProps: any,
  5. owner: null | Fiber,
  6. mode: TypeOfMode,
  7. expirationTime: ExpirationTime,
  8. ): Fiber {
  9. let fiber;
  10. let fiberTag = IndeterminateComponent;
  11. // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
  12. let resolvedType = type;
  13. if (typeof type === 'function') {
  14. if (shouldConstruct(type)) {
  15. fiberTag = ClassComponent;
  16. }
  17. } else if (typeof type === 'string') {
  18. fiberTag = HostComponent;
  19. } else {
  20. getTag: switch (type) {
  21. //....
  22. }
  23. }
  24. fiber = createFiber(fiberTag, pendingProps, key, mode);
  25. fiber.elementType = type;
  26. fiber.type = resolvedType;
  27. fiber.expirationTime = expirationTime;
  28. return fiber;
  29. }

在createFiberFromTypeAndProps中经过计算得到fiberTag为ClassComponent。确定fiberTag 后,通过createFiber得到FiberNode。

FiberNode

  1. const createFiber = function(tag,pendingProps,key,mode){
  2. // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
  3. return new FiberNode(tag, pendingProps, key, mode);
  4. };
  5. //packages\react-reconciler\src\ReactFiber.js
  6. function FiberNode(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode) {
  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.contextDependencies = null;
  24. this.mode = mode;
  25. // Effects
  26. this.effectTag = NoEffect;
  27. this.nextEffect = null;
  28. this.firstEffect = null;
  29. this.lastEffect = null;
  30. this.expirationTime = NoWork;
  31. this.childExpirationTime = NoWork;
  32. this.alternate = null;
  33. if (enableProfilerTimer) {
  34. this.actualDuration = 0;
  35. this.actualStartTime = -1;
  36. this.selfBaseDuration = 0;
  37. this.treeBaseDuration = 0;
  38. }
  39. }

React tree 其实是 fiber 的集合,也是FiberNode的集合。FiberNode与 domNode 基本上是对应的。

placeSingleChild

  1. function placeSingleChild(newFiber: Fiber): Fiber {
  2. if (shouldTrackSideEffects && newFiber.alternate === null) {
  3. newFiber.effectTag = Placement;
  4. }
  5. return newFiber;
  6. }

执行完 reconcileSingleElement 得到 FiberNode,并未添加effect。placeSingleChild 判断新建的fiber是否添加 effect。

next newFiber

执行完placeSingleChild 后就回到了workLoop上,此时 nextUnitOfWork 的值为App Component的fiber,值为:
image.png
接下来执行的栈就是:
workLoop -> performUnitOfWork -> beginWork( ClassComponent ) -> updateClassComponent
此时 updateClassComponent 的参数如下:
current 为null;
workInProgress 为 nextUnitOfWork ;
Component 是 workInProgress.type 值为 f App();
nextProps 是 workInProgress.pendingProps 值为 {};

  1. function updateClassComponent(current, workInProgress,Component,
  2. nextProps,renderExpirationTime ) {
  3. const instance = workInProgress.stateNode;
  4. let shouldUpdate;
  5. if (instance === null) {
  6. if (current !== null) {
  7. current.alternate = null;
  8. workInProgress.alternate = null;
  9. workInProgress.effectTag |= Placement;
  10. }
  11. constructClassInstance(workInProgress,Component,nextProps,renderExpirationTime);
  12. mountClassInstance(workInProgress,Component,nextProps,renderExpirationTime);
  13. shouldUpdate = true;
  14. } else if (current === null) { //...} else {//... }
  15. const nextUnitOfWork = finishClassComponent(current,workInProgress,Component,
  16. shouldUpdate,hasContext,renderExpirationTime);
  17. return nextUnitOfWork;
  18. }

updateClassComponent 中的方法已在beginWork演示。此处需要注意的是 finishClassComponent 中的 nextChildren = instance.render(),也就是在此执行 App 中的render函数。在执行 instance.render 时会触发相关的校验,此处不再深入。执行完render得到 nextChildren 为:
image.png
接着就是执行 reconcileChildren。reconcileChildren参数current为null,执行mountChildFibers。mountChildFibers的shouldTrackSideEffects 为false,不再追踪effects。mountChildFibers 和 reconcileChildFibers 类似。

  1. function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
  2. if (current$$1 === null) {
  3. workInProgress.child = mountChildFibers(workInProgress, null,
  4. nextChildren, renderExpirationTime);
  5. }
  6. }

reconcileChildFibers中的 newChild.$$typeof 为 REACT_ELEMENT_TYPE,执行的也是reconcileSingleElement,和上面的App Component 类似,得到 nextUnitOfWork 是一个type为”div”的fiber,其结构如下:
image.png
tag为5是HostComponent,beginWork中执行 updateHostComponent。在 updateHostComponent 需要注意的是shouldSetTextContent,当 typeof props.children === ‘string’ 则将nextChildren设置为null。

  1. export function shouldSetTextContent(type: string, props: Props): boolean {
  2. return (
  3. type === 'textarea' || type === 'option' || type === 'noscript' ||
  4. typeof props.children === 'string' || typeof props.children === 'number' ||
  5. (typeof props.dangerouslySetInnerHTML === 'object' &&
  6. props.dangerouslySetInnerHTML !== null &&
  7. props.dangerouslySetInnerHTML.__html != null)
  8. );
  9. }

这是处理 HostComponent 较为特殊的点。若此时 typeof props.children !== ‘string’,则workInProgress.child不为null。也就是说 div元素是一个fiber,文本也可以是一个fiber,这个根据children的type而定。

nextChildren为null,照常执行。在 deleteRemainingChildren 中返回null。
image.png
此时 beginWork 返回 null,接下来就进入到 complete Work。