1.forwardRef

用来解决高阶组件传递ref的问题。它的用法就像是使用redux的时候,在外面包裹一层,通过...props的方式把外部的props传入到实际组件

const TargetComponent = React.forwardRef((props, ref) => (
  <TargetComponent ref={ref} />
))

2.ReactElement

ReactElement通过createElement创建,调用该方法需要传入三个参数:

  • type
  • config
  • children

type指代这个ReactElement的类型

  • 字符串比如divp代表原生DOM,称为HostComponent
  • Class类型是我们继承自Component或者PureComponent的组件,称为ClassComponent
  • 方法就是functional Component
  • 原生提供的FragmentAsyncMode等是Symbol,会被特殊处理
  • TODO: 是否有其他的

从源码可以看出虽然创建的时候都是通过config传入的,但是keyref不会跟其他config中的变量一起被处理,而是单独作为变量出现在ReactElement上。

从源码可以看出虽然创建的时候都是通过config传入的,但是keyref不会跟其他config中的变量一起被处理,而是单独作为变量出现在ReactElement上。在最后创建ReactElement我们看到了这么一个变量$$typeof,在这里他是一个常量:REACT_ELEMENT_TYPE

3.React Children

React上面有有一个Children对象,上面有着Children定义

const React = {
  Children: {
    map,
    forEach,
    count,
    toArray,
    only,
  },
	......
}

找到定义

function mapChildren(children, func, context) {
  if (children == null) {
    return children
  }
  const result = []
  mapIntoWithKeyPrefixInternal(children, result, null, func, context)
  return result
}

function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
  let escapedPrefix = ''
  if (prefix != null) {
    escapedPrefix = escapeUserProvidedKey(prefix) + '/'
  }
  const traverseContext = getPooledTraverseContext(
    array,
    escapedPrefix,
    func,
    context,
  )
  traverseAllChildren(children, mapSingleChildIntoContext, traverseContext)
  releaseTraverseContext(traverseContext)
}

可以看到后面调用了getPooledTraverseContext,这是从内容池中找到当前的context对象,操作完了之后releaseTraverseContext会把当前的context对象清空然后放回到pool中。然后调用traverseAllChildren,这个方法的作用就是递归调用mapChildren将所有Children铺平,直到当前元素是一个有效的ReactElement

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
  const { result, keyPrefix, func, context } = bookKeeping

  let mappedChild = func.call(context, child, bookKeeping.count++)
  if (Array.isArray(mappedChild)) {
    mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c)
  } else if (mappedChild != null) {
    if (isValidElement(mappedChild)) {
      mappedChild = cloneAndReplaceKey(
        mappedChild,
        keyPrefix +
          (mappedChild.key && (!child || child.key !== mappedChild.key)
            ? escapeUserProvidedKey(mappedChild.key) + '/'
            : '') +
          childKey,
      )
    }
    result.push(mappedChild)
  }
}

使用方法

React.Children.map(this.props.children, function (child) {
    return <li>{child}</li>;
})

4.React中的数据结构

Update &UpdateQueue

export type Update<State> = {
  // 更新的过期时间
  expirationTime: ExpirationTime,

  // export const UpdateState = 0;
  // export const ReplaceState = 1;
  // export const ForceUpdate = 2;
  // export const CaptureUpdate = 3;
  // 指定更新的类型,值为以上几种
  tag: 0 | 1 | 2 | 3,
  // 更新内容,比如`setState`接收的第一个参数
  payload: any, // 当前DOM元素?
  // 对应的回调,`setState`,`render`都有
  callback: (() => mixed) | null,

  // 指向下一个更新
  next: Update<State> | null,
  // 指向下一个`side effect`
  nextEffect: Update<State> | null,
};

export type UpdateQueue<State> = {
  // 每次操作完更新之后的`state`
  baseState: State,

  // 队列中的第一个`Update`
  firstUpdate: Update<State> | null,
  // 队列中的最后一个`Update`
  lastUpdate: Update<State> | null,

  // 第一个捕获类型的`Update`
  firstCapturedUpdate: Update<State> | null,
  // 最后一个捕获类型的`Update`
  lastCapturedUpdate: Update<State> | null,

  // 第一个`side effect`
  firstEffect: Update<State> | null,
  // 最后一个`side effect`
  lastEffect: Update<State> | null,

  // 第一个和最后一个捕获产生的`side effect`
  firstCapturedEffect: Update<State> | null,
  lastCapturedEffect: Update<State> | null,
};

5.初次ReactDOM.reander过程

首先调用legacyRenderSubtreeIntoContainer

ReactDOM = {
  render(
    element: React$Element<any>,
    container: DOMContainer,
    callback: ?Function,
  ) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    )
  },
}
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {
  let root: Root = (container._reactRootContainer: any)
  if (!root) {
    // 生成root,当前传入的根container的_reactRootContainer指向生成的root
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    )
    if (typeof callback === 'function') {
      const originalCallback = callback
      callback = function() {
        const instance = DOMRenderer.getPublicRootInstance(root._internalRoot)
        originalCallback.call(instance)
      }
    }
    // 首次render不比unbatchedUpdates
    DOMRenderer.unbatchedUpdates(() => {
      if (parentComponent != null) {
        // 一般不会出现
      } else {
        root.render(children, callback)
      }
    })
  } else {
    // 有root的情况
  }
  return DOMRenderer.getPublicRootInstance(root._internalRoot)
}

legacyCreateRootFromDOMContainer方法生成root,返回的结果上的_internalRoot指向这个root。同时会调用DOMRenderer.createContainer创建FiberRoot,然后生成的rootcurrent属性指向这个FiberRoot

function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): Root {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container)
  // First clear any existing content.
  if (!shouldHydrate) {
    let warned = false
    let rootSibling
    while ((rootSibling = container.lastChild)) {
      container.removeChild(rootSibling)
    }
  }
  // Legacy roots are not async by default.
  const isConcurrent = false
  return new ReactRoot(container, isConcurrent, shouldHydrate)
}

function ReactRoot(
  container: Container,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  const root = DOMRenderer.createContainer(container, isConcurrent, hydrate)
  this._internalRoot = root
}

ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot
  const work = new ReactWork()
  callback = callback === undefined ? null : callback
  if (__DEV__) {
    warnOnInvalidCallback(callback, 'render')
  }
  if (callback !== null) {
    work.then(callback)
  }
  DOMRenderer.updateContainer(children, root, null, work._onCommit)
  return work
}

可以看到还有个updateContainer方法

export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current
  const currentTime = requestCurrentTime()
  const expirationTime = computeExpirationForFiber(currentTime, current)
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  )
}

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)
}

可以看到先生成一个时间const expirationTime,也就是这次更新的超时时间,然后调用updateContainerAtExpirationTime,然后调用scheduleRootUpdate进行调度

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) {
    warningWithoutStack(
      typeof callback === 'function',
      'render(...): Expected the last optional `callback` argument to be a ' +
        'function. Instead received: %s.',
      callback,
    )
    update.callback = callback
  }
  enqueueUpdate(current, update)

  scheduleWork(current, expirationTime)
  return expirationTime
}

scheduleRootUpdate会调用createUpdate创建Update对象,其playloadcontainer上面的ReactNodeList,然后调用enqueueUpdate生成一个双向列表队列,然后调用scheduleWork开始调用。上面的过程都是依赖的FiberRoot

6.expirationTime

import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';

export type ExpirationTime = number;

export const NoWork = 0;
export const Never = 1;
export const Sync = MAX_SIGNED_31_BIT_INT;

const UNIT_SIZE = 10;
const MAGIC_NUMBER_OFFSET = MAX_SIGNED_31_BIT_INT - 1;

// 1 unit of expiration time represents 10ms.
export function msToExpirationTime(ms: number): ExpirationTime {
  // Always add an offset so that we don't clash with the magic number for NoWork.
  return MAGIC_NUMBER_OFFSET - ((ms / UNIT_SIZE) | 0);
}

export function expirationTimeToMs(expirationTime: ExpirationTime): number {
  return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}

function ceiling(num: number, precision: number): number {
  return (((num / precision) | 0) + 1) * precision;
}

function computeExpirationBucket(
  currentTime,
  expirationInMs,
  bucketSizeMs,
): ExpirationTime {
  return (
    MAGIC_NUMBER_OFFSET -
    ceiling(
      MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE,
      bucketSizeMs / UNIT_SIZE,
    )
  );
}

export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;

export function computeAsyncExpiration(
  currentTime: ExpirationTime,
): ExpirationTime {
  return computeExpirationBucket(
    currentTime,
    LOW_PRIORITY_EXPIRATION,
    LOW_PRIORITY_BATCH_SIZE,
  );
}

export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150;
export const HIGH_PRIORITY_BATCH_SIZE = 100;

export function computeInteractiveExpiration(currentTime: ExpirationTime) {
  return computeExpirationBucket(
    currentTime,
    HIGH_PRIORITY_EXPIRATION,
    HIGH_PRIORITY_BATCH_SIZE,
  );
}

最后的计算公式为((((currentTime - 2 + 5000 / 10) / 25) | 0) + 1) * 25(公式存疑),也就是说最后的结果是以25ms为单位向上增加的,在25ms内,react这样做为的是让两次时间非常想尽的更新得到同样的expirationTime,然后他们的优先级就会判定一样,就会再一次更新中完成,也就是一个batchedUpdates

计算当前currentTime

function requestCurrentTime() {
  if (isRendering) {
    return currentSchedulerTime
  }
  findHighestPriorityRoot()
  if (
    nextFlushedExpirationTime === NoWork ||
    nextFlushedExpirationTime === Never
  ) {
    recomputeCurrentRendererTime()
    currentSchedulerTime = currentRendererTime
    return currentSchedulerTime
  }
  return currentSchedulerTime
}

react为了避免每次计算expirationTime都要Date now,所以用currentRendererTime来保存了这个值,上面的currentSchedulerTime也是记录这个值的

首先

if (isRendering) {
  return currentSchedulerTime
}

这个isRendering只有在performWorkOnRoot的时候才会被设置为true,而其本身是一个同步的方法,不存在他执行到一半没有设置isRenderingfalse的时候就跳出,那么什么情况下会在这里出现新的requestCurrentTime呢?

  • 在生命周期方法中调用了setState
  • 需要挂起任务的时候

也就是说 React 要求在一次rendering过程中,新产生的update用于计算过期时间的current必须跟目前的renderTime保持一致,同理在这个周期中所有产生的新的更新的过期时间都会保持一致!

  findHighestPriorityRoot()
  if (
    nextFlushedExpirationTime === NoWork ||
    nextFlushedExpirationTime === Never
  ) {
    recomputeCurrentRendererTime()
    currentSchedulerTime = currentRendererTime
    return currentSchedulerTime
  }

findHighestPriorityRoot找到优先级最高的节点,同时设置nextFlushedExpirationTime,然后if判断当前队列中没有更新的任务,就会重新计算当前时间,否则,还是返回最初的currentSchedulerTime

7.各种expirationTime

  • root.expirationTime
  • root.nextExpirationTimeToWorkOn
  • root.childExpirationTime
  • root.earliestPendingTime & root.lastestPendingTime
  • root.earliestSuspendedTime & root.lastestSuspendedTime
  • root.lastestPingedTime
  • nextFlushedExpirationTime
  • nextLatestAbsoluteTimeoutMs
  • currentRendererTime
  • currentSchedulerTime

React更新是从FilberRoot开始的,所以每次更新时都会遍历向上查找FiberRoot,而这个childExpirationTime会设置到每个父节点,代表其子节点expirationTime

pendingTime,在FiberRoot上有两个值earliestPendingTimelastestPedingTime,他们是一对值,用来记录所有子树中需要进行渲染的更新的expirationTime的区间

suspendedTime,同样的在ReactFiber上有两个值earliestSuspendedTimelastestSuspendedTime这两个值是用来记录被挂起的任务的过期时间的

首先我们定义一下什么情况下任务是被挂起的:

  • 出现可捕获的错误并且还有优先级更低的任务的情况下
  • 当捕获到thenable,并且需要设置onTimeout的时候

root.expirationTime是用来标志当前渲染的过期时间的,请注意他只管本渲染周期,他并不管你现在的渲染目标是哪个,渲染目标是由root.nextExpirationTimeToWorkOn来决定的。

那么他们有什么区别呢?主要区别在于发挥作用的阶段

expirationTime作用于调度阶段,主要指责是:

  • 决定是异步执行渲染还是同步执行渲染
  • 作为react-schedulertimeout标准,决定是否要优先渲染

nextExpirationTimeToWorkOn主要作用于渲染阶段:

  • 决定那些更新要在当前周期中被执行
  • 通过跟每个节点的expirationTime比较决定该节点是否可以直接bailout(跳过)

他们都是通过pendingTimesuspenededTimepingedTime中删选出来的,唯一的不同是,nextExpirationTimeToWorkOn在没有pending或者pinged的任务的时候会选择最晚的suspendedTime,而expirationTime会选择最早的

expirationTime的变化:

  • scheduleWork的时候通过markPendingExpirationTime设置
  • beginWork的时候被设置为NoWork
  • onUncaughtError的时候设置为NoWork
  • onSuspend的时候又会设置回当次更新的expirationTime

8.scheduleWork

上面说的在创建完更新队列后,就会调用scheduleWork调度了

function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
  const root = scheduleWorkToRoot(fiber, expirationTime);
  if (root === null) {
    if (__DEV__) {
      switch (fiber.tag) {
        case ClassComponent:
          warnAboutUpdateOnUnmounted(fiber, true);
          break;
        case FunctionComponent:
        case ForwardRef:
        case MemoComponent:
        case SimpleMemoComponent:
          warnAboutUpdateOnUnmounted(fiber, false);
          break;
      }
    }
    return;
  }
	
  // 此条件的意思是当前处理空闲阶段,没有render或者commit,并且下一次任务不为空,还有当前过期时间(优先级)高于下次的过期时间
  // 则清空队列
  // 例如上一个任务是异步任务(优先级很低,超时时间是 502ms),并且在上一个时间片(初始是 33ms)任务没有执行完,而且等待下一次requestIdleCallback的时候新的任务进来了,并且超时时间很短(52ms 或者 22ms 甚至是 Sync),那么优先级就变成了先执行当前任务,也就意味着上一个任务被打断了(interrupted)
  if (
    !isWorking &&
    nextRenderExpirationTime !== NoWork &&
    expirationTime > nextRenderExpirationTime
  ) {
    interruptedBy = fiber;
    resetStack();
  }
  // 记录所有子节点的任务时间区间
  markPendingPriorityLevel(root, expirationTime);
  // 要么处于没有 work 的状态,要么只能在 render 阶段,要么有两个不同的root
  if (
    !isWorking ||
    isCommitting ||
    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;
    invariant(
      false,
      'Maximum update depth exceeded. This can happen when a ' +
        'component repeatedly calls setState inside ' +
        'componentWillUpdate or componentDidUpdate. React limits ' +
        'the number of nested updates to prevent infinite loops.',
    );
  }
}

首先是调用scheduleWorkToRoot,这个方法的主要是更新当前任务对应Filber的到期时间

function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {
  recordScheduleUpdate();

  if (__DEV__) {
    if (fiber.tag === ClassComponent) {
      const instance = fiber.stateNode;
      warnAboutInvalidUpdates(instance);
    }
  }

  // 如果filber上的过期时间小于当前的过期时间,更新
  if (fiber.expirationTime < expirationTime) {
    fiber.expirationTime = expirationTime;
  }
  let alternate = fiber.alternate;
  if (alternate !== null && alternate.expirationTime < expirationTime) {
    alternate.expirationTime = expirationTime;
  }
  // fiber.return 返回当前fiber的父节点
  let node = fiber.return;
  let root = null;
  if (node === null && fiber.tag === HostRoot) {
    // 如果filber.return的值为null,设置root的值为根节点(fiber.stateNode)
    root = fiber.stateNode;
  } else {
    // 循环向上遍历,更新每个子节点的过期时间
    // 如果子节点的过期时间小于当前过期时间,则更新
    while (node !== null) {
      alternate = node.alternate;
      if (node.childExpirationTime < expirationTime) {
        node.childExpirationTime = expirationTime;
        if (
          alternate !== null &&
          alternate.childExpirationTime < expirationTime
        ) {
          alternate.childExpirationTime = expirationTime;
        }
      } else if (
        alternate !== null &&
        alternate.childExpirationTime < expirationTime
      ) {
        alternate.childExpirationTime = expirationTime;
      }
      if (node.return === null && node.tag === HostRoot) {
        root = node.stateNode;
        break;
      }
      node = node.return;
    }
  }
  ......
  return root;
}

requestWork

function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
  // 将root加入调度队列中,不会存在两个相同的 root 前后出现在队列中
  addRootToSchedule(root, expirationTime)
  if (isRendering) {
    return
  }

  if (isBatchingUpdates) {
    if (isUnbatchingUpdates) {
      nextFlushedRoot = root
      nextFlushedExpirationTime = Sync
      performWorkOnRoot(root, Sync, true)
    }
    return
  }

  // 根据expirationTime调用performSyncWork还是scheduleCallbackWithExpirationTime

  if (expirationTime === Sync) {
    performSyncWork()
  } else {
    // 异步调度,并且当前已经有异步任务,如果当前传入的任务优先级高,取消之前的调度,异步调度当前任务
    // 无异步任务,生成一个到期时间,调度
    scheduleCallbackWithExpirationTime(root, expirationTime)
  }
}

9.performWork

performAsyncWork 为异步调用,一开始的shouldYieldToRenderer代表是否当前调度中有没有时间执行异步任务,若shouldYieldToRenderer返回false,则代表有,那么重新计算当前的expirationTime,更新所有的已过期的root任务的nextExpirationTimeToWorkOn

let didYield: boolean = false;
function shouldYieldToRenderer() {
  if (didYield) {
    return true;
  }
  if (shouldYield()) {
    didYield = true;
    return true;
  }
  return false;
}

function performAsyncWork() {
  try {
    if (!shouldYieldToRenderer()) {
      // 如果当前还有调度的链表
      if (firstScheduledRoot !== null) {
        // 重新计算当前的一个expirationTime
        recomputeCurrentRendererTime();
        let root: FiberRoot = firstScheduledRoot;
        do {
          // 设置所有的已过期的root任务的nextExpirationTimeToWorkOn为当前计算出的expirationTime
          didExpireAtExpirationTime(root, currentRendererTime);
          root = (root.nextScheduledRoot: any);
        } while (root !== firstScheduledRoot);
      }
    }
    performWork(NoWork, true);
  } finally {
    didYield = false;
  }
}

performSyncWork

同步调度任务,方法很简单,直接调用performWork

function performSyncWork() {
  performWork(Sync, false);
}

performWork

此方法为真正的调度方法,首先会调用findHighestPriorityRoot,找到当前队列优先级最高的root任务,命名为nextFlushedRoot,已经其对应的expirationTime,命名为nextFlushedExpirationTime

function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
  findHighestPriorityRoot();

  if (isYieldy) {
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;
		
    if (enableUserTimingAPI) {
      const didExpire = nextFlushedExpirationTime > currentRendererTime;
      const timeout = expirationTimeToMs(nextFlushedExpirationTime);
      stopRequestCallbackTimer(didExpire, timeout);
    }

    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      minExpirationTime <= nextFlushedExpirationTime &&
      !(didYield && currentRendererTime > nextFlushedExpirationTime)
    ) {
      performWorkOnRoot(
        nextFlushedRoot,
        nextFlushedExpirationTime,
        currentRendererTime > nextFlushedExpirationTime,
      );
      findHighestPriorityRoot();
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
    }
  } else {
    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      minExpirationTime <= nextFlushedExpirationTime
    ) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
      findHighestPriorityRoot();
    }
  }
  if (isYieldy) {
    callbackExpirationTime = NoWork;
    callbackID = null;
  }
  if (nextFlushedExpirationTime !== NoWork) {
    scheduleCallbackWithExpirationTime(
      ((nextFlushedRoot: any): FiberRoot),
      nextFlushedExpirationTime,
    );
  }
  finishRendering();
}

可以看到,不管同步异步,都有

while(
 nextFlushedRoot !== null &&
 nextFlushedExpirationTime !== NoWork &&
 minExpirationTime <= nextFlushedExpirationTime
)

这是一个循环调度root任务的过程,判断条件为调度队列不为空,并且当前传入的任务优先级小于优先级最高的任务。异步调用时还有个额外的条件!(didYield && currentRendererTime > nextFlushedExpirationTime),翻译过来就是当前传入的异步任务没过期,并且当前的渲染时间也没超过优先级最高的任务。然后这个循环就会在每次循环中调用performWorkOnRoot

10.performWorkOnRoot

这里是整个调度过程的提交、render过程。不同的是,在异步任务中有个判断

if (!shouldYieldToRenderer()) {
  completeRoot(root, finishedWork, expirationTime);
} else {
  root.finishedWork = finishedWork;
}

意思就是说如果当前没有时间处理异步任务,直接completeRoot

function performWorkOnRoot(
  root: FiberRoot,
  expirationTime: ExpirationTime,
  isYieldy: boolean,
) {
  invariant(
    !isRendering,
    'performWorkOnRoot was called recursively. This error is likely caused ' +
      'by a bug in React. Please file an issue.',
  );

  isRendering = true;

  if (!isYieldy) {

    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      completeRoot(root, finishedWork, expirationTime);
    } else {
      root.finishedWork = null;
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        cancelTimeout(timeoutHandle);
      }
      renderRoot(root, isYieldy);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        completeRoot(root, finishedWork, expirationTime);
      }
    }
  } else {
    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      completeRoot(root, finishedWork, expirationTime);
    } else {
      root.finishedWork = null;
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        cancelTimeout(timeoutHandle);
      }
      renderRoot(root, isYieldy);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        if (!shouldYieldToRenderer()) {
          completeRoot(root, finishedWork, expirationTime);
        } else {
          root.finishedWork = finishedWork;
        }
      }
    }
  }
  isRendering = false;
}

11.renderRoot

这个判断的意思就是如果当前没有其他的任务,那么就进行一些初始化的工作

if (
    expirationTime !== nextRenderExpirationTime ||
    root !== nextRoot ||
    nextUnitOfWork === null
  ) {
    resetStack();
    nextRoot = root;
    nextRenderExpirationTime = expirationTime;
    nextUnitOfWork = createWorkInProgress(
      nextRoot.current,
      null,
      nextRenderExpirationTime,
    );
		root.pendingCommitExpirationTime = NoWork;
		......
}

然后调用workLoop,对于异步任务,仍要判断是否有剩余时间处理此任务

  do {
    try {
      workLoop(isYieldy);
    } catch (thrownValue) {
      
    }
    break;
  } while (true);
function workLoop(isYieldy) {
  if (!isYieldy) {
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  }
}

如果在这过程中会有报错,则停止调度,会在上面的catch中处理,并且设置finishedWorknull,从而结束这一次的render

performUnitOfWork

function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  const current = workInProgress.alternate;

  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) {
      stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
    }
  } else {
    next = beginWork(current, workInProgress, nextRenderExpirationTime);
    workInProgress.memoizedProps = workInProgress.pendingProps;
  }
  if (next === null) {
    next = completeUnitOfWork(workInProgress);
  }
	
  ......
  ReactCurrentOwner.current = null;

  return next;
}

首先执行next = beginWork(current, workInProgress, nextRenderExpirationTime)对节点进行操作,其内部会针对不同类型的节点进行创建,返回的结果即会赋值给nextreturn next。 如果next === null,说明子节点已经处理完,调用next = completeUnitOfWork(workInProgress)

completeUnitOfWork会从下往上遍历,处理effact tag,如果到root节点返回的是null,代表整棵树的遍历已经结束了,如果找到了其他兄弟节点,就返回这个节点,重新走一遍workLoop

12.updateClassComponent

beginWork中,会针对不同的节点类型来调用不同的方法处理,这里以ClassComponent为例

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps,
  renderExpirationTime: ExpirationTime,
) {
	......
  // 对context进行处理
  let hasContext;
  // 如果当前组件是context provider
  // 设置hasContext标记
  if (isLegacyContextProvider(Component)) {
    hasContext = true;
    pushLegacyContextProvider(workInProgress);
  } else {
    hasContext = false;
  }
  prepareToReadContext(workInProgress, renderExpirationTime);

  const instance = workInProgress.stateNode;
  let shouldUpdate;
  // 如果实例为空
  if (instance === null) {
    if (current !== null) {
      current.alternate = null;
      workInProgress.alternate = null;
      workInProgress.effectTag |= Placement;
    }
    constructClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    );
    // 挂载这个实例
    mountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    );
    shouldUpdate = true;
  } else if (current === null) {
    // 第一次渲染时,current为null
    // 重新挂载实例
    shouldUpdate = resumeMountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    );
  } else {
    // 更新类组件的时候
    // 调用updateClassInstance
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    );
  }
  ......
  // 调用finishClassComponent
  // 如果需要更新,调用reconcileChildren,返回更新后的workInProgress
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderExpirationTime,
  );
  return nextUnitOfWork;
}

constructClassInstance

function constructClassInstance(
  workInProgress: Fiber,
  ctor: any,
  props: any,
  renderExpirationTime: ExpirationTime,
): any {
  .......
  adoptClassInstance(workInProgress, instance);
}

adoptClassInstance

function adoptClassInstance(workInProgress: Fiber, instance: any): void {
  instance.updater = classComponentUpdater;
  workInProgress.stateNode = instance;
  setInstance(instance, workInProgress);
	......
}
  
const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
  },
  enqueueReplaceState(inst, payload, callback) {
  },
  enqueueForceUpdate(inst, callback) {
};

classComponentUpdater里面几个方法,即对应着setStatereplaceStateforceUpdate

mountClassInstance

function mountClassInstance(
  workInProgress: Fiber,
  ctor: any,
  newProps: any,
  renderExpirationTime: ExpirationTime,
): void {
  if (__DEV__) {
    checkClassInstance(workInProgress, ctor, newProps);
  }

  const instance = workInProgress.stateNode;
  // 初始化 props、state 等实例属性
  instance.props = newProps;
  instance.state = workInProgress.memoizedState;
  instance.refs = emptyRefsObject;

  const contextType = ctor.contextType;
  if (typeof contextType === 'object' && contextType !== null) {
    instance.context = readContext(contextType);
  } else {
    const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
    instance.context = getMaskedContext(workInProgress, unmaskedContext);
  }
	.......
	// 如果有updateQueue就更新
  let updateQueue = workInProgress.updateQueue;
  if (updateQueue !== null) {
    processUpdateQueue(
      workInProgress,
      updateQueue,
      newProps,
      instance,
      renderExpirationTime,
    );
    instance.state = workInProgress.memoizedState;
  }
	
	// 如果定义了getDerivedStateFromProps周期函数,则调用
  const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    );
    instance.state = workInProgress.memoizedState;
  }

  if (
    typeof ctor.getDerivedStateFromProps !== 'function' &&
    typeof instance.getSnapshotBeforeUpdate !== 'function' &&
    (typeof instance.UNSAFE_componentWillMount === 'function' ||
      typeof instance.componentWillMount === 'function')
  ) {
    callComponentWillMount(workInProgress, instance);
    updateQueue = workInProgress.updateQueue;
    if (updateQueue !== null) {
      processUpdateQueue(
        workInProgress,
        updateQueue,
        newProps,
        instance,
        renderExpirationTime,
      );
      instance.state = workInProgress.memoizedState;
    }
  }

  // 判断是否有componentDidMount,
	// componentDidMount要在真正渲染进DOM之后才调用,也就是commit之后
  if (typeof instance.componentDidMount === 'function') {
    workInProgress.effectTag |= Update;
  }
}

13.commitRoot

调用完renderRoot之后,fiber中对应的每个节点都执行了render之前所有的生命周期函数,并创建了对应的DOM元素,放在workInProgress.stateNode中。然后调用commitRoot,提交子树中优先级最高的任务,更新DOM元素的属性后,挂载组件,调用componentDidMount

参考链接

react源码解析