React源码解析之beginWork Fiber的更新入口

1,879 阅读9分钟

前言

紧接上一篇的内容React源码解析之Fiber,上一篇分析了Fiber树的创建,接下来则是进入Fiber的节点的创建和更新。

正文

对Fiber的节点的创建和更新会调用performUnitOfWork函数,这个函数中主要是调用两个方法:beginWorkcompleteUnitOfWork,本章先只分析beginWork

beginWork的主要工作,则是从root fiber开始向下深度遍历构建workInprogress树,进行diff算法确定需要更新的fiber的最终状态。

function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

function performUnitOfWork(unitOfWork: Fiber): void {
  const current = unitOfWork.alternate;

  let next;
  ...
  next = beginWork(current, unitOfWork, subtreeRenderLanes);

  ...
  
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }

  ReactCurrentOwner.current = null;
}

记得在React源码解析之Fiber中,最后形成的Fiber树结构如下:

6df7502be7a648278187f05d6bcd5f0e_tplv-k3u1fbpfcp-watermark.webp

在调用performUnitOfWork函数时传入workInProgress,也就是root fiber,然后在performUnitOfWork函数中获取workInProgress树上的root fiberalternate属性,也就是current树上的root fiber,后面会以current树上对应workInProgress树上的Fiber节点是否存在来判断该Fiber节点是更新,还是创建。简单理解就是Fiber节点上alternate属性是否为null,不为null则是更新,为null则是创建。

然后调用beginWork函数传入,两颗树上对应的Fiber节点,current是current树上的Fiber节点,unitOfWorkworkInProgress树上的Fiber节点,以及渲染的Lane优先级集合subtreeRenderLanes

beginWork(current, unitOfWork, subtreeRenderLanes);
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
  ...

  // 判断current是否为null,不为null表示更新,为null则表示是创建
  if (current !== null) {
    const oldProps = current.memoizedProps; // 上一次更新的props
    const newProps = workInProgress.pendingProps; // 本次更新的props
    // didReceiveUpdate  表示是否有新的props更新,有则会设置为true,没有则是false
    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      // Force a re-render if the implementation changed due to hot reload:
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      didReceiveUpdate = true;
    } else {
      // checkScheduledUpdateOrContext函数检查当前fiber节点上的lanes是否存在于renderLanes中
      // 存在则说明当前fiber节点需要更新,不存在则不需要更新则复用之前的节点
      const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
        current,
        renderLanes,
      );
      if (
        !hasScheduledUpdateOrContext &&
        (workInProgress.flags & DidCapture) === NoFlags
      ) {
        didReceiveUpdate = false;
        // 复用之前的节点
        return attemptEarlyBailoutIfNoScheduledUpdate(
          current,
          workInProgress,
          renderLanes,
        );
      }
      if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
        didReceiveUpdate = true;
      } else {
        didReceiveUpdate = false;
      }
    }
  } else {
    didReceiveUpdate = false;
  }

  workInProgress.lanes = NoLanes;

  switch (workInProgress.tag) {
    case ClassComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderLanes,
      );
    }
   ...
  }
 
}

首先判断current是否为null,如果为null则说明是创建,不为null,则会调用checkScheduledUpdateOrContext函数检查当前fiber节点上的lanes是否存在于renderLanes中,存在则说明当前fiber节点需要更新返回true,不存在则不需要更新返回false。

function checkScheduledUpdateOrContext(
  current: Fiber,
  renderLanes: Lanes,
): boolean {
  const updateLanes = current.lanes;
  // 判断当前Fiber极端上的lanes是否存在于renderLanes上,存在则说明该Fiber节点需要更新,反之不需要
  if (includesSomeLane(updateLanes, renderLanes)) {
    return true;
  }
 
  ...
  return false;
}

checkScheduledUpdateOrContext函数返回false,则会调用attemptEarlyBailoutIfNoScheduledUpdate函数,这个函数主要作用,则是复用fiber节点。

checkScheduledUpdateOrContext函数返回true,则会根据Fiber节点上tag属性来判断类型,我们以classComponent为例,则会调用updateClassComponent

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {
 ...

  const instance = workInProgress.stateNode;
  let shouldUpdate;

  //根据组件stateNode(组件实例)的值是否为null,以此来判断应该创建组件还是更新组件
  if (instance === null) {
    if (current !== null) {
      current.alternate = null;
      workInProgress.alternate = null;
      workInProgress.flags |= Placement;
    }
    // 实例化组件,将组件实例与对应的fiber节点相关联
    constructClassInstance(workInProgress, Component, nextProps);
    // 将fiber上的state和props更新至组件实例上
    // 并且会检查是否声明了getDerivedStateFromProps生命周期函数
    // 有的话则会调用并且使用getDerivedStateFromProps生命周期函数中返回的state来更新组件实例上的state
    // 检查是否声明了componentDidmount生命周期,有的话则会收集标示添加到fiber的flags属性上
    mountClassInstance(workInProgress, Component, nextProps, renderLanes);
    // 创建组件肯定是需要更新的,所以直接为shouldUpdate赋值为true
    shouldUpdate = true;
  } else if (current === null) {
    shouldUpdate = resumeMountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  } else {
    // 更新组件-updateClassInstance函数:
    // 调用生命周期:getDerivedStateFromProps,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate
    // 执行更新队列,把将要更新的state的值与老的state的值进行合并,合并完成和会把新的state的值挂载到workInProgress.memoizedState属性上
    // 使用新的值分别更新组件实例中props,state的值
    // 收集Effect:会判断当前类组件是否声明了生命周期函数:componentDidUpdate,getSnapshotBeforeUpdate,
    // 声明了则会将相对应的flag添加到当前fiber节点的flags属性上
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  }

  // finishClassComponent函数:
  // 判断生命周期shouldComponentUpdate返回的值为false的话,则复用之前的fiber节点
  // 为true则会调用render函数,获取使用最新的state和props生成的React element
  // 调用reconcileChildren函数,这个函数主要作用是进行diff算法。
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderLanes,
  );
 
  return nextUnitOfWork;
}

创建类组件

可以看到首先会通过stateNode属性获取类组件的实例,然后判断实例是否为null,为null则说明是创建,则需要去实例化组件:

// 实例化组件,将组件实例与对应的fiber节点相关联
constructClassInstance(workInProgress, Component, nextProps);

function constructClassInstance(
  workInProgress: Fiber,
  ctor: any,
  props: any,
): any {
 ...
  // 实例化组件
  let instance = new ctor(props, context);
  ...
  const state = (workInProgress.memoizedState =
    instance.state !== null && instance.state !== undefined
      ? instance.state
      : null);
  
  // 将fiber节点与组件实例相互关联,在之后更新时可复用
  adoptClassInstance(workInProgress, instance);
  ...
  return instance;
}

function adoptClassInstance(workInProgress: Fiber, instance: any): void {
  ...
  // fiber节点的stateNode属性指向该fiber节点对应的组件实例对象
  workInProgress.stateNode = instance;
 
  // 将组件实例与fiber关联在一起,instance._reactInternals = workInProgress
  setInstance(instance, workInProgress);
}

调用constructClassInstance时传入fiber,组件的class,和props,然后constructClassInstance中通过实例化组件class获取组件实例,然后获取组件实例中的state属性赋值给workInProgress.memoizedState,之后调用adoptClassInstance函数,将fiber节点与组件实例相互关联。在下一次更新时,则可以通过instance._reactInternals获取到组件实例对应的Fiber节点。

创建组件完成后,会调用mountClassInstance函数:

function mountClassInstance(
  workInProgress: Fiber,
  ctor: any,
  newProps: any,
  renderLanes: Lanes,
): void {
 ...

  const instance = workInProgress.stateNode;
  instance.props = newProps;
  instance.state = workInProgress.memoizedState;
  instance.refs = emptyRefsObject;

  initializeUpdateQueue(workInProgress);

 ...

  // 检查当前组件是否声明了getDerivedStateFromProps生命周期函数
  const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  if (typeof getDerivedStateFromProps === 'function') {
    // 有声明的话则会调用并且使用getDerivedStateFromProps生命周期函数中返回的state来更新workInProgress.memoizedState
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    );
    // 将更新了的state赋值给组件实例的state属性
    instance.state = workInProgress.memoizedState;
  }

  //调用componentWillMount生命周期
  if (
    typeof ctor.getDerivedStateFromProps !== 'function' &&
    typeof instance.getSnapshotBeforeUpdate !== 'function' &&
    (typeof instance.UNSAFE_componentWillMount === 'function' ||
      typeof instance.componentWillMount === 'function')
  ) {
    callComponentWillMount(workInProgress, instance);
    // If we had additional state updates during this life-cycle, let's
    // process them now.
    processUpdateQueue(workInProgress, newProps, instance, renderLanes);
    instance.state = workInProgress.memoizedState;
  }

  // 判断是否声明了componentDidMount声明周期,声明了则会添加标识Update至flags中,在commit阶段调用
  if (typeof instance.componentDidMount === 'function') {
    let fiberFlags: Flags = Update;
    if (enableSuspenseLayoutEffectSemantics) {
      fiberFlags |= LayoutStatic;
    }
    ...
    workInProgress.flags |= fiberFlags;
  }
}

这个函数主要的作用是:

  1. fiber上的state和props更新至组件实例上,并且会检查是否声明了getDerivedStateFromProps生命周期函数,有的话则会调用并且使用getDerivedStateFromProps生命周期函数中返回的state来更新组件实例上的state和Fiber节点上的memoizedState。
  2. 检查是否声明了componentWillMount生命周期,有的话则会调用componentWillMount生命周期
  3. 检查是否声明了componentDidmount生命周期,有的话则会收集flag添加到fiber的flags属性上

调用完成后,因为是创建组件,肯定是需要更新的,所以直接为shouldUpdate赋值为true

更新类组件

更新组件,则会调用updateClassInstance函数:

function updateClassInstance(
  current: Fiber,
  workInProgress: Fiber,
  ctor: any,
  newProps: any,
  renderLanes: Lanes,
): boolean {
  const instance = workInProgress.stateNode;

  cloneUpdateQueue(current, workInProgress);

  const unresolvedOldProps = workInProgress.memoizedProps;
  const oldProps =
    workInProgress.type === workInProgress.elementType
      ? unresolvedOldProps
      : resolveDefaultProps(workInProgress.type, unresolvedOldProps);
  instance.props = oldProps;
  const unresolvedNewProps = workInProgress.pendingProps;

  const oldContext = instance.context;
  const contextType = ctor.contextType;
  let nextContext = emptyContextObject;
  if (typeof contextType === 'object' && contextType !== null) {
    nextContext = readContext(contextType);
  } else if (!disableLegacyContext) {
    const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
    nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
  }

  const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  const hasNewLifecycles =
    typeof getDerivedStateFromProps === 'function' ||
    typeof instance.getSnapshotBeforeUpdate === 'function';

  
  if (
    !hasNewLifecycles &&
    (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
      typeof instance.componentWillReceiveProps === 'function')
  ) {
    if (
      unresolvedOldProps !== unresolvedNewProps ||
      oldContext !== nextContext
    ) {
      callComponentWillReceiveProps(
        workInProgress,
        instance,
        newProps,
        nextContext,
      );
    }
  }

  resetHasForceUpdateBeforeProcessing();

  const oldState = workInProgress.memoizedState;
  let newState = (instance.state = oldState);
  // 执行更新队列,把将要更新的state的值与老的state的值进行合并
  // 合并完成和会把新的state的值挂载到workInProgress.memoizedState属性上
  processUpdateQueue(workInProgress, newProps, instance, renderLanes);
  newState = workInProgress.memoizedState;

  if (
    unresolvedOldProps === unresolvedNewProps &&
    oldState === newState &&
    !hasContextChanged() &&
    !checkHasForceUpdateAfterProcessing() &&
    !(
      enableLazyContextPropagation &&
      current !== null &&
      current.dependencies !== null &&
      checkIfContextChanged(current.dependencies)
    )
  ) {
    
    if (typeof instance.componentDidUpdate === 'function') {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.flags |= Update;
      }
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.flags |= Snapshot;
      }
    }
    return false;
  }

  if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    );
    newState = workInProgress.memoizedState;
  }

  // checkHasForceUpdateAfterProcessing是获取是否是强制更新的标识,也就是调用的forceUpdate方法创建的更新
  // checkShouldComponentUpdate是调用组件内声明的shouldComponentUpdate生命周期方法,判断是否需要更新
  const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState,
      nextContext,
    ) ||
    (enableLazyContextPropagation &&
      current !== null &&
      current.dependencies !== null &&
      checkIfContextChanged(current.dependencies));

  if (shouldUpdate) {
   
    if (
      !hasNewLifecycles &&
      (typeof instance.UNSAFE_componentWillUpdate === 'function' ||
        typeof instance.componentWillUpdate === 'function')
    ) {
      if (typeof instance.componentWillUpdate === 'function') {
        instance.componentWillUpdate(newProps, newState, nextContext);
      }
      if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
        instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
      }
    }
    if (typeof instance.componentDidUpdate === 'function') {
      workInProgress.flags |= Update;
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      workInProgress.flags |= Snapshot;
    }
  } else {
  
    if (typeof instance.componentDidUpdate === 'function') {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.flags |= Update;
      }
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.flags |= Snapshot;
      }
    }

    workInProgress.memoizedProps = newProps;
    workInProgress.memoizedState = newState;
  }

  // 使用新的值分别更新组件中props,state的值
  instance.props = newProps;
  instance.state = newState;
  instance.context = nextContext;

  return shouldUpdate;
}

这个函数主要的作用:

  1. 会调用componentWillReceiveProps函数
  2. 通过调用processUpdateQueue执行更新队列,把将要更新的state的值与老的state的值进行合并,合并完成和会把新的state的值挂载到workInProgress.memoizedState属性上,然后使用新的值分别更新组件实例中props,state的值
  3. 调用checkHasForceUpdateAfterProcessing函数获取是否是强制更新的标识,也就是调用的forceUpdate方法创建的更新,checkShouldComponentUpdate是调用组件内声明的shouldComponentUpdate生命周期方法,判断是否需要更新
  4. 收集生命周期:会判断当前类组件是否声明了生命周期函数:componentDidUpdategetSnapshotBeforeUpdate,声明了则会将相对应的flag添加到当前fiber节点的flags属性上,在commit阶段调用

finishClassComponent函数

不管更新还是创建,最后都会调用finishClassComponent函数,这个函数的作用:

  1. 判断生命周期shouldComponentUpdate返回的值为false的话,则复用之前的fiber节点
  2. 为true则会调用render函数,获取使用最新的state和props生成的React element
  3. 调用reconcileChildren函数,这个函数主要作用是进行diff算法,确定需要更新的fiber的最终状态。
function finishClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  shouldUpdate: boolean,
  hasContext: boolean,
  renderLanes: Lanes,
) {
  // 判断是否有ref,有的话则会收集flag到flags上,在comit阶段执行
  markRef(current, workInProgress);
  ...

  const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;

  // 判断shouldUpdate为false的话,则复用之前的fiber节点
  if (!shouldUpdate && !didCaptureError) {
   ...

    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }

  const instance = workInProgress.stateNode;

  let nextChildren;
  
  ...
  
  // 调用组件render方法获取到react element
  nextChildren = instance.render();

  ...
  
  // diff算法
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);

 ...

  return workInProgress.child;
}

最后返回子Fiber节点,进行beginWork。reconcileChildren函数会在写diff算法时单独进行分析。

总结

beginWork做了哪些事情:

  1. 创建workInProgress树,然后根据current树深度遍历构建workInProgress树
  2. 会将新旧state和props进行合并更新
  3. 调用生命周期:getDerivedStateFromProps,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render
  4. 收集生命周期,在commit阶段调用:componentDidmount,componentDidUpdate,getSnapshotBeforeUpdate
  5. 调用reconcileChildren函数进行diff算法,确定需要更新的fiber的最终状态。