beginWork - fiber的更新 
在上一章中,beginWork阶段对每一个不同的标签都创建或复用了对应的fiber,接下来就是对fiber进行一些更加精细的处理。
updateClassComponent 
prepareToReadContext(workInProgress, renderLanes);第一步是判断context是否有更新,如果有的话那么标记didReceiveUpdate为true。接着获取instance:
const instance = workInProgress.stateNode;
if (instance === null) {
  constructClassInstance(workInProgress, Component, nextProps);
  mountClassInstance(workInProgress, Component, nextProps, renderLanes);
}instance为class组件的实例,存储在fiber.stateNode上。如果instance不存在,说明还没有被实例化。
constructClassInstance方法 
首先第一步是判断是否有contextType属性且为对象形式。如果存在说明使用了context,此时会进行readContext,将读取得到的context形成链表形式,存放到fiber.dependencies.firstContext上:
const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
  context = readContext((contextType: any));
}随后对class进行实例化,并获取class组件的state,存放到fiber.memoizedState属性上:
let instance = new ctor(props, context);
const state = (workInProgress.memoizedState =
  instance.state !== null && instance.state !== undefined
    ? instance.state
    : null);最后执行adoptClassInstance方法,建立fiber和instance的联系。并且为instance添加updater,即setState、forceUpdate等方法。
instance.updater = classComponentUpdater;
// class fiber 的 stateNode 指向的是 class 实例
workInProgress.stateNode = instance;
// 将 fiber 存储到实例的_reactInternals 属性上
setInstance(instance, workInProgress);mountClassInstance方法 
首先会读取context值,存放到instance.context上:
const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
  // 因此在 class 组件里可以通过 this.context 访问 context
  instance.context = readContext(contextType);
}随后是生命周期的执行,首先是静态方法getDerivedStateFromProps的执行:
let partialState = getDerivedStateFromProps(nextProps, prevState);
const memoizedState =
  partialState === null || partialState === undefined
    ? prevState
  : Object.assign({}, prevState, partialState);
workInProgress.memoizedState = memoizedState;该方法返回的state会和class组件原有的state合并,并且会覆盖原有的state。
接下来的判断就是为了兼容componentWillMount,但是已经不推荐使用了。
if (
  typeof ctor.getDerivedStateFromProps !== 'function' &&
  typeof instance.getSnapshotBeforeUpdate !== 'function' &&
  (typeof instance.UNSAFE_componentWillMount === 'function' ||
    typeof instance.componentWillMount === 'function')
) {}最后如果有componentDidMount,那么会将fiber标记为Update,这个标记只是为了后续触发该生命周期,并不是有什么更新操作:
if (typeof instance.componentDidMount === 'function') {
  let fiberFlags: Flags = Update;
  workInProgress.flags |= fiberFlags;
}resumeMountClassInstance方法 
当instance存在current不存在时,可以复用instance。与mountClassInstance不同的是它会首先判断是否有componentWillReceiveProps方法(已不推荐使用),如果存在则会调用。
其次是执行update队列,更新state:
const oldState = workInProgress.memoizedState;
let newState = (instance.state = oldState);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
newState = workInProgress.memoizedState;如果前后没有更新过,那么只会在componentDidMount存在时标记为Update。
if (
  oldProps === newProps &&
  oldState === newState &&
  !hasContextChanged() &&
  !checkHasForceUpdateAfterProcessing()
) {
  if (typeof instance.componentDidMount === 'function') {
    let fiberFlags: Flags = Update;
    if (enableSuspenseLayoutEffectSemantics) {
      fiberFlags |= LayoutStatic;
    }
    workInProgress.flags |= fiberFlags;
  }
  return false
}否则,会调用getDerivedStateFromProps方法。调用完成后再次判断是否有更新,并在componentDidMount存在时标记为Update。
const shouldUpdate =
  // hasForUpdate
  checkHasForceUpdateAfterProcessing() ||
  // 对 shouldComponentUpdate 和 isPureReactComponent 进行判断
  checkShouldComponentUpdate(
    workInProgress,
    ctor,
    oldProps,
    newProps,
    oldState,
    newState,
    nextContext,
  );updateClassInstance方法 
updateClassInstance与resumeMountClassInstance大同小异,区别在于使用componentDidUpdate时将fiber标记为Update。
finishClassComponent方法 
finishClassComponent方法最主要的工作是执行render方法,然后reconcileChildren。
const instance = workInProgress.stateNode;
nextChildren = instance.render();
reconcileChildren(current, workInProgress, nextChildren, renderLanes);updateContextProvider 
在React包中定义了createContext方法,该方法返回一个context:
export function createContext<T>(defaultValue: T): ReactContext<T> {
  const context: ReactContext<T> = {
    $$typeof: REACT_CONTEXT_TYPE,
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    _threadCount: 0,
    Provider: (null: any),
    Consumer: (null: any),
  };
  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  return context;
}因此jsx在解析Provider的时候,得到的type为:
{
  $$typeof: REACT_PROVIDER_TYPE,
   _context: context,
}更新时,会获取Provider上定义的value属性,并进行对比:
if (is(oldValue, newValue)) {
  if (
   oldProps.children === newProps.children &&
   !hasLegacyContextChanged()
  ) {
   return bailoutOnAlreadyFinishedWork(
    current,
        workInProgress,
        renderLanes,
      );
    }
} else {
 // context 改变了,寻找 consumer 、 classComponent 并标记 更新
  propagateContextChange(workInProgress, context, renderLanes);
}如果前后的value值未发生变化,那么跳过该节点的协调,否则调用propagateContextChange方法,该方法会以当前节点为根节点向下进行深度遍历。如果节点fiber上的dependencies存在,说明使用了context。遍历dependencies看是否有使用Provider变动对应的context。
 if (dependency.context === context) {}如果当前的fiber.tag === ClassComponent,由于context变化,所以会创建一个update,添加到fiber.updateQueue.shared.pending当中去。除此之外,还会进行lane的合并,这样子节点就能进行相应的更新了。
fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
list.lanes = mergeLanes(list.lanes, renderLanes);如果查找到节点为Provider,且它们的type相同(意味着是同一Provider),那么就会跳过,不做任何处理。
else if (fiber.tag === ContextProvider) {
 // 遇到下一个 Provider 停止向下递归
 nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
}updateContextConsumer 
如果遇到的是Consumer标签,首先会执行prepareToReadContext方法,但是此时是不存在dependencies,因此没有:
const firstContext = dependencies.firstContext;
if (firstContext !== null) {
  if (includesSomeLane(dependencies.lanes, renderLanes)) {
    markWorkInProgressReceivedUpdate();
  }
  dependencies.firstContext = null;
}判断dependencies.firstContext是否存在且lane与renderLanes有交集,有的话说明还有待更新的内容,此时将didReceiveUpdate设为true,firstContext置为空。
紧接着readContext,这里的type就是createContext创建的context:
let context: ReactContext<any> = workInProgress.type;
const newValue = readContext(context);readContext的过程就是将context添加到dependencies当中并形成链表结构:
const contextItem = {
 context: ((context: any): ReactContext<mixed>),
 memoizedValue: value,
 next: null,
}
   
lastContextDependency = contextItem;
currentlyRenderingFiber.dependencies = {
 lanes: NoLanes,
 firstContext: contextItem,
};随后将从context中读取到的value进行渲染,达到value传递的目的:
newChildren = render(newValue);最终得到可以使用value的children。
updateFunctionComponent 
对于function组件,第一步也是处理context:
prepareToReadContext(workInProgress, renderLanes);第二步是调用renderWithHooks方法,其主要目的是执行函数,在执行的过程中处理hooks。
// 赋值 hooks dispatcher。
ReactCurrentDispatcher.current =
  current === null || current.memoizedState === null
    ? HooksDispatcherOnMount
    : HooksDispatcherOnUpdate;
let children = Component(props, secondArg);这里的第一个参数props好理解,第二个参数secondArg在forwardRef的时候为ref。另外关于hooks相关的内容后续章节会单独讲解。
updateForwardRef 
forwardRef方法在React包中定义:
export function forwardRef<Props, ElementType: React$ElementType>(
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  // 相当于通过 render 创建了一个节点
  const elementType = {
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };
  return elementType;
}在beginWork的时候先获取到render,然后其他操作同函数式组件,会进行renderWithHooks。
const render = Component.render;
const ref = workInProgress.ref;
nextChildren = renderWithHooks(
  current,
  workInProgress,
  render,
  nextProps,
  ref,
  renderLanes,
)唯一不同的是这个位置传入了ref值,在调用render的时候,第二个参数存在并且为传入的ref。
let children = Component(props, secondArg);updateHostComponent 
updateHostComponent方法比较简单,主要代码为:
const isDirectTextChild = shouldSetTextContent(type, nextProps);
// ...
reconcileChildren(current, workInProgress, nextChildren, renderLanes);通过shouldSetTextContent判断节点是否可以按照文本节点处理,相当于做了一个小优化。
updateFragment 
updateFragment也比较简单,相当于只包装了一层空壳,不对这层壳做处理,直接处理其子元素:
const nextChildren = workInProgress.pendingProps;
reconcileChildren(current, workInProgress, nextChildren, renderLanes);updateMemoComponent 
memo方法在React包中定义:
export function memo<Props>(
  type: React$ElementType,
  compare?: (oldProps: Props, newProps: Props) => boolean,
) {
  const elementType = {
    $$typeof: REACT_MEMO_TYPE,
    type,
    compare: compare === undefined ? null : compare,
  };
  return elementType;
}如果current不存在,说明不可复用,此时需要为被memo的组件创建fiber:
const child = createFiberFromTypeAndProps(
 Component.type,
 null,
 nextProps,
 workInProgress,
 workInProgress.mode,
 renderLanes,
);如果current存在,那么current.child就是被memo包裹的组件的fiber。接着判断组件有没有被更新:
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
  current,
  renderLanes,
);如果被更新了,那么会通过createWorkInProgress创建(或复用)之前的fiber。否则通过传入的compare函数比较是否需要更新:
if (!hasScheduledUpdateOrContext) {
  const prevProps = currentChild.memoizedProps;
  let compare = Component.compare;
  compare = compare !== null ? compare : shallowEqual;
  if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }
}updatePortalComponent 
createPortal方法在React-Dom包中定义:
export function createPortal(
  children: ReactNodeList,
  containerInfo: any,
  implementation: any,
  key: ?string = null,
): ReactPortal {
  return {
    $$typeof: REACT_PORTAL_TYPE,
    key: key == null ? null : '' + key,
    children,
    containerInfo,
    implementation,
  };
}在创建fiber时,它的stateNode属性是一个有containerInfo属性的对象:
export function createFiberFromPortal(
  portal: ReactPortal,
  mode: TypeOfMode,
  lanes: Lanes,
): Fiber {
  const pendingProps = portal.children !== null ? portal.children : [];
  const fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
  fiber.lanes = lanes;
  fiber.stateNode = {
    containerInfo: portal.containerInfo,
    pendingChildren: null, // Used by persistent updates
    implementation: portal.implementation,
  };
  return fiber;
}在beginWork更新时,会将containerInfo存放起来:
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);在它的子节点被添加时,可以找到这个containerInfo节点。这样就可以达到fiber在rootFiber内,但是添加的真实节点在其他节点的目的。
mountLazyComponent 
lazy的定义在React包中,主要由两个函数构成:
function lazyInitializer<T>(payload: Payload<T>): T {
  if (payload._status === Uninitialized) {
    const ctor = payload._result;
    const thenable = ctor();
    thenable.then(
      moduleObject => {
        if (payload._status === Pending || payload._status === Uninitialized) {
          const resolved: ResolvedPayload<T> = (payload: any);
          resolved._status = Resolved;
          resolved._result = moduleObject;
        }
      },
      error => {
        if (payload._status === Pending || payload._status === Uninitialized) {
          const rejected: RejectedPayload = (payload: any);
          rejected._status = Rejected;
          rejected._result = error;
        }
      },
    );
    if (payload._status === Uninitialized) {
      const pending: PendingPayload = (payload: any);
      pending._status = Pending;
      pending._result = thenable;
    }
  }
  if (payload._status === Resolved) {
    // 如果 promise 完成了,那么会将加载的模块返回
    const moduleObject = payload._result;
    return moduleObject.default;
  } else {
    // 第一次加载时,报错
    throw payload._result;
  }
}
export function lazy<T>(
  ctor: () => Thenable<{default: T, ...}>,
): LazyComponent<T, Payload<T>> {
  const payload: Payload<T> = {
    _status: -1,
    _result: ctor,
  };
 // 返回的 type
  const lazyType: LazyComponent<T, Payload<T>> = {
    $$typeof: REACT_LAZY_TYPE,
    _payload: payload,
    _init: lazyInitializer,
  };
  return lazyType;
}在beginWork的时候,会正式执行该promise:
const lazyComponent: LazyComponentType<any, any> = elementType;
const payload = lazyComponent._payload;
const init = lazyComponent._init;
let Component = init(payload);第一次执行时,由于promise没有resolve,因此会报错,此时会被外围的catch捕捉,该过程后续会在Suspense章节中详细讲解。
如果能正常加载该组件,那么会根据组件的类型去调用对应的函数:
workInProgress.type = Component;
// 获取 tag
const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
const resolvedProps = resolveDefaultProps(Component, props);
let child;
switch (resolvedTag) {
  case FunctionComponent: { // ... }
  case ClassComponent: { // ... }
  case ForwardRef: { // ... }
  case MemoComponent: { // ... }
}