React16 diff 功能源码剖析

1,581 阅读18分钟

github项目地址(配合源码阅读更加) react-research


1 初次渲染构建的 Fiber Tree

在上节(React 初次渲染源码剖析)中会讲到 react 在初次渲染时会产生出一颗完整的 Fiber 树,大致流程如下:

react 每次渲染都会依赖两颗树,一颗是 workInProgress(工作树),另一颗是 root.current(当前树)
更新操作都基于工作树去完成,首先每次更新的时候都会创建一个 update 对象,并且将其挂载在指定的 FibeNode 的 updateQuene 下面,首次更新 update 对象的 payload 为 React.render 方法接受的第一个参数 element,之后来到 workLoopSync 采用深度优先遍历的方式去 reconcile(调节)将每个子组件转化成子 Fiber 节点,逐步构建出完整的 fiber tree( 也称之为 finishedWork,初次渲染完成时工作树中除树中第一个节点(类似于 wrapper)之外的每个节点的 alternate 属性都为 null,最后在 commitRoot 中将工作树替换成为当前树。

first render

2 setState 原理

setState 是 Component 类的原型方法,在第一次渲染中 reconcile 将 fiber 节点与 instance 实例进行了关联,并给每个实例设置了相同的 updater 属性,调用原型 setState 方法将会调用 updater 对象的 enqueueSetState 方法,enqueueSetState 方法中通过实例获取到对应的 fiber 节点,将 update 对象置入后安排更新

// 类组件
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater;
}

// setState原型方法
Component.prototype.setState = function (partialState, callback) {
  this.updater.enqueueSetState(this, partialState, callback, "setState");
};

// updater对象
const classComponentUpdater = {
  // ...
  enqueueSetState(inst, payload, callback) {
    // 获取对应的Fiber节点
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const suspenseConfig = requestCurrentSuspenseConfig();
    const lane = requestUpdateLane(fiber, suspenseConfig);

    // 创建update对象
    const update = createUpdate(eventTime, lane, suspenseConfig);
    update.payload = payload;
    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, "setState");
      }
      update.callback = callback;
    }

    // 将update对象置入updateQueue
    enqueueUpdate(fiber, update);
    // 安排更新
    scheduleUpdateOnFiber(fiber, lane);
  },
  // ...
};

function getInstance(key) {
  return key._reactInternals;
}

function setInstance(key, value) {
  key._reactInternals = value;
}

function adoptClassInstance(workInProgress, instance) {
  instance.updater = classComponentUpdater;
  workInProgress.stateNode = instance;
  // 将类实例与Fiber节点关联起来
  setInstance(instance, workInProgress);
}

3 标记目标节点更新

传统模式(Legacy Mode)传入的 lane 为 SyncLane 等于 1 标记当前节点 lanes 合并传入的 lane
更新当前节点的父节点的 childLanes 合并的传入 lane \

这样一来目标节点(触发 setState 的节点)的 lanes 与根节点的 lanes 都包含 SyncLane

function markUpdateLaneFromFiberToRoot(fiber, lane) {
  // 更新当前fiber节点的lanes,合并传入的lane
  fiber.lanes = mergeLanes(fiber.lanes, lane);
  let alternate = fiber.alternate;
  // 第一次setState时alternate为null
  if (alternate !== null) {
    alternate.lanes = mergeLanes(alternate.lanes, lane);
  }

  let node = fiber.return;
  let root = null;
  if (node === null && fiber.tag === HostRoot) {
    root = fiber.stateNode;
  } else {
    while (node !== null) {
      alternate = node.alternate;
      // 更新当前fiber节点的childLanes,合并传入的lane
      node.childLanes = mergeLanes(node.childLanes, lane);
      if (alternate !== null) {
        alternate.childLanes = mergeLanes(alternate.childLanes, lane);
      }
      // 找到根节点
      if (node.return === null && node.tag === HostRoot) {
        root = node.stateNode;
        break;
      }
      node = node.return;
    }
  }

  if (root !== null) {
    // 标记根节点有一个等待中的更新
    markRootUpdated(root, lane);
    if (workInProgressRoot === root) {
      if (
        deferRenderPhaseUpdateToNextBatch ||
        (executionContext & RenderContext) === NoContext
      ) {
        workInProgressRootUpdatedLanes = mergeLanes(
          workInProgressRootUpdatedLanes,
          lane
        );
      }
      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
        markRootSuspended(root, workInProgressRootRenderLanes);
      }
    }
  }

  return root;
}

function mergeLanes(a, b) {
  return a | b;
}

function markRootUpdated(root, updateLane) {
  root.pendingLanes |= updateLane;

  const higherPriorityLanes = updateLane - 1;
  root.suspendedLanes &= higherPriorityLanes;
  root.pingedLanes &= higherPriorityLanes;
}

4 准备新的工作树

react 在每次更新的时候都会产生新的 workInProgress(工作树),之后的更新操作都要基于这颗工作树,所以要提前准备好。

function prepareFreshStack(root, lanes) {
  root.finishedWork = null;
  root.finishedLanes = NoLanes;

  const timeoutHandle = root.timeoutHandle;
  if (timeoutHandle !== noTimeout) {
    root.timeoutHandle = noTimeout;
    cancelTimeout(timeoutHandle);
  }

  if (workInProgress !== null) {
    let interruptedWork = workInProgress.return;
    while (interruptedWork !== null) {
      unwindInterruptedWork(interruptedWork);
      interruptedWork = interruptedWork.return;
    }
  }
  workInProgressRoot = root;
  // 根据当前树生成新的workInProgress
  workInProgress = createWorkInProgress(root.current, null);
  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
  workInProgressRootExitStatus = RootIncomplete;
  workInProgressRootFatalError = null;
  workInProgressRootLatestProcessedEventTime = -1;
  workInProgressRootLatestSuspenseTimeout = -1;
  workInProgressRootCanSuspendUsingConfig = null;
  workInProgressRootSkippedLanes = NoLanes;
  workInProgressRootUpdatedLanes = NoLanes;
  workInProgressRootPingedLanes = NoLanes;

  if (enableSchedulerTracing) {
    spawnedWorkDuringRender = null;
  }
}

// 这是很重要的一个函数,主要功能是从当前树拷贝一份节点并且置入新的pendingProps
function createWorkInProgress(current, pendingProps) {
  // 两颗树交替使用 上次渲染的当前树转换成这次渲染的工作树
  let workInProgress = current.alternate;

  if (workInProgress === null) {
    // 两棵树交替使用被称之为双重缓冲池技术(double buffering pooling technique)
    // 因为最多只需要两个版本的树,有另外一颗未使用的数可以自由的重用
    // 这是懒创建为了避免分配额外的对象对于一些永不更新的事情
    // 这也是我们能够回收额外的内存如果需要的话
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode
    );
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;

    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.pendingProps = pendingProps;
    // 需要,因为Blocks store date依赖type
    workInProgress.type = current.type;

    // 我们已经有一个备份了,重置effect tag
    workInProgress.effectTag = NoEffect;

    // 副作用链表不再有效
    workInProgress.nextEffect = null;
    workInProgress.firstEffect = null;
    workInProgress.lastEffect = null;
  }

  // 拷贝其余的属性
  workInProgress.childLanes = current.childLanes;
  workInProgress.lanes = current.lanes;

  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;

  // 克隆dependencies object。这将会在渲染阶段被使用,所以不能跟当前树节点分享
  const currentDependencies = current.dependencies_new;
  workInProgress.dependencies_new =
    currentDependencies === null
      ? null
      : {
          lanes: currentDependencies.lanes,
          firstContext: currentDependencies.firstContext,
          responders: currentDependencies.responders,
        };

  // 这些将会在父结点调节(reconciliation)的时候重写
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;

  if (enableProfilerTimer) {
    workInProgress.selfBaseDuration = current.selfBaseDuration;
    workInProgress.treeBaseDuration = current.treeBaseDuration;
  }

  return workInProgress;
}

5 构建新的工作树

react 在更新的时候需要重新构建一份新的工作树(workInProgress),从而与更新之前的当前树(current)进行 diff,所以本节所有内容都是讲述如何构建新的工作树。

5.1 克隆不需要更新的 Fiber 节点

触发更新的节点在第三步中已经更新 lanes 为 1,而不需要更新的父节点们 lanes 还是为 0 但是不需要更新的父节点们的 childLanes 被设置为 1,此处将无需更新的节点进行了克隆处理,因为第四步中生成新的工作树将 workInProgress.child 指向 current.child,所以此时子节点完全一致,为了防止修改工作树影响到当前树,也为了保持树的完整性,必须克隆一份新的节点。需要注意的是这里传入父节点返回子节点,并且将工作树节点的 alternate 指向当前树节点。新的工作树中并不是所有的 Fiber 节点都需要更新,只有标记为更新的节点以及其所有子节点需要更新。 react 在此处的策略是:
① 目标更新的节点(包括自己)和它的所有父节点,在当前树中浅拷贝一份节点(防止修改一棵树的节点影响到另一棵树的节点)
② 目标更新的节点的子节点,重新 reconcile(调节),包括重新设置节点类型或者设置新的 pendingProps 如果是类组件还要调用 update 相关生命。
至此,一颗新的完整的工作树就构建完成了

function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

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

  let next;
  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
    startProfilerTimer(unitOfWork);
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
  } else {
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
  }

  // 将工作树每个节点的memoizedProps和pendingProps先设置为一样
  unitOfWork.memoizedProps = unitOfWork.pendingProps;

  if (next === null) {
    // If this doesn't spawn new work, complete the current work.
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }

  ReactCurrentOwner.current = null;
}

function beginWork(current, workInProgress, renderLanes) {
  const updateLanes = workInProgress.lanes;

  if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      didReceiveUpdate = true;
      //                              1            0
    } else if (!includesSomeLane(renderLanes, updateLanes)) {
      // 判断是否需要更新 如果renderLanes不包含updateLanes则不需要更新 进入此处
      // ...
      didReceiveUpdate = false;
      return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    }
  }

  // ...
}

function includesSomeLane(a, b) {
  return (a & b) !== NoLanes;
}

function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
  if (current !== null) {
    workInProgress.dependencies_new = current.dependencies_new;
  }

  if (enableProfilerTimer) {
    stopProfilerTimerIfRunning(workInProgress);
  }

  markSkippedUpdateLanes(workInProgress.lanes);

  // 在第三步中将父节点的childLanes设置为SyncLane(1)
  // 检查子节点是否有等待中的任务(子节点是否需要更新)
  if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
    return null;
  } else {
    // 子节点需要更新
    // 这个fiber节点没有任何工作,但是它的子节点有,克隆这个子节点并继续
    // 在第四步中,将workInProgress.child指向current.child所以此时两棵树的子节点是一致的即修改工作树节点也会修改到当前树节点所以必须要执行克隆操作
    // 克隆时还会将工作树节点的alternate指向当前树节点进行关联
    cloneChildFibers(current, workInProgress);
    return workInProgress.child;
  }
}

function cloneChildFibers(current, workInProgress) {
  if (workInProgress.child === null) {
    return;
  }

  let currentChild = workInProgress.child;
  let newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
  workInProgress.child = newChild;

  newChild.return = workInProgress;
  while (currentChild.sibling !== null) {
    currentChild = currentChild.sibling;
    newChild = newChild.sibling = createWorkInProgress(
      currentChild,
      currentChild.pendingProps
    );
    newChild.return = workInProgress;
  }
  newChild.sibling = null;
}

5.2 处理需要更新的 ClassComponent 节点

当处理完不需要更新的父节点之后,就轮到需要更新的节点了
在第三步中目标节点 lanes 被标记为更新,工作树的节点是从当前树中拷贝的,因此不满足第一个第二个 if 进入第三个 else
else 中没有 return 进入类组件更新方法(updateClassComponent)
类组件是不会对应真实的 DOM 的所以真正的 diff 不在此处,只是为子节点准备好新的 props(通过 render 方法得到)
由于在 5.2 中将工作树中每个节点的 memoizedProps 和 pendingProps 先设置为一样,而此处修改了 pendingProps,所以子节点们永远进入第一个 if,因为它们的 memoizedProps 和 pendingProps 对象地址不可能会相同。
在这一步中有两个处理 ClassComponent 的子结点的策略:
① 比较新生成的 element 和当前树节点的 key,如果不相同直接删除不进入策略 ②
② 如果组件类型相同,希望对应两颗子树有相同的结构,所以直接从当前树中拷贝一份,反之根据组件重新创建对应的 fiber 节点

function beginWork(current, workInProgress, renderLanes) {
  const updateLanes = workInProgress.lanes;

  if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      // Force a re-render if the implementation changed due to hot reload:
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      // 第一个if
      didReceiveUpdate = true;
    } else if (!includesSomeLane(renderLanes, updateLanes)) {
      // 第二个if
      didReceiveUpdate = false;
      // ...
    } else {
      // 第三个else
      // 第一个lane被标记为1的节点进入这里
      didReceiveUpdate = false;
    }
  } else {
    didReceiveUpdate = false;
  }

  // 在处理之前将lanes设置为NoLanes表示已经处理过更新了
  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
      );
    }
    // ...
  }
}

function updateClassComponent(
  current,
  workInProgress,
  Component,
  nextProps,
  renderLanes
) {
  let hasContext;
  if (isLegacyContextProvider(Component)) {
    hasContext = true;
    pushLegacyContextProvider(workInProgress);
  } else {
    hasContext = false;
  }
  prepareToReadContext(workInProgress, renderLanes);

  const instance = workInProgress.stateNode;

  let shouldUpdate;
  // 实例为空
  if (instance === null) {
    // 当前树节点不为空
    if (current !== null) {
      current.alternate = null;
      workInProgress.alternate = null;
      workInProgress.effectTag |= Placement;
    }
    // In the initial pass we might need to construct the instance.
    // 调用组件构造方法初始化实例
    constructClassInstance(workInProgress, Component, nextProps);
    // 挂载类实例 调用componentWillMount生命周期
    mountClassInstance(workInProgress, Component, nextProps, renderLanes);
    shouldUpdate = true;
  } else if (current === null) {
    // 重新挂载
    shouldUpdate = resumeMountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderLanes
    );
  } else {
    // setState后进入这里,判断是否需要更新,如果需要更新实例和节点中的props和state,以及调用一些生命周期
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes
    );
  }
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderLanes
  );
  return nextUnitOfWork;
}

function updateClassInstance(
  current,
  workInProgress,
  ctor,
  newProps,
  renderLanes
) {
  function cloneUpdateQueue(current, workInProgress) {
    // 从当前树节点浅拷贝update queue,除非已经浅拷贝过了
    const queue = workInProgress.updateQueue;
    const currentQueue = current.updateQueue;
    if (queue === currentQueue) {
      const clone = {
        baseState: currentQueue.baseState,
        firstBaseUpdate: currentQueue.firstBaseUpdate,
        lastBaseUpdate: currentQueue.lastBaseUpdate,
        shared: currentQueue.shared,
        effects: currentQueue.effects,
      };
      workInProgress.updateQueue = clone;
    }
  }

  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;

  // context相关暂时不了解
  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";

  // 如果有用getDerivedStateFromProps和getSnapshotBeforeUpdate两个新的生命周期
  // 就不会调用就旧的componentWillReceiveProps生命周期
  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
  processUpdateQueue(workInProgress, newProps, instance, renderLanes);
  newState = workInProgress.memoizedState;

  // 如果props、state和context都没更新且不是强制更新
  // 视为更新完成,修改effectTag标识需要调用componentDidUpdate和getSnapshotBeforeUpdate生命周期
  if (
    unresolvedOldProps === unresolvedNewProps &&
    oldState === newState &&
    !hasContextChanged() &&
    !checkHasForceUpdateAfterProcessing()
  ) {
    if (typeof instance.componentDidUpdate === "function") {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Update;
      }
    }
    if (typeof instance.getSnapshotBeforeUpdate === "function") {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Snapshot;
      }
    }
    return false;
  }

  // 调用getDerivedStateFromProps生命周期
  if (typeof getDerivedStateFromProps === "function") {
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps
    );
    newState = workInProgress.memoizedState;
  }

  // 判断是否需要更新
  // 如果是强制更新这个值肯定是true
  // 否则,如果是普通的Component调用实例componentShouldUpdate生命周期(如果有)判断是否需要更新
  // PureComponent会进行一层浅比较,其他情况默认为需要更新
  const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState,
      nextContext
    );

  // 普通的Component如果无componentShouldUpdate生命周期,是必须要更新的
  if (shouldUpdate) {
    // true
    // 如果没有使用新的生命周期,调用实例componentWillUpdate方法
    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.effectTag |= Update;
    }
    // 如果实例有getSnapshotBeforeUpdate生命周期方法,在节点的effectTag标记,之后调用
    if (typeof instance.getSnapshotBeforeUpdate === "function") {
      workInProgress.effectTag |= Snapshot;
    }
  } else {
    // 不需要更新 不解释了
    if (typeof instance.componentDidUpdate === "function") {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Update;
      }
    }
    if (typeof instance.getSnapshotBeforeUpdate === "function") {
      if (
        unresolvedOldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Snapshot;
      }
    }

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

  // 更新实例props和state以及context
  instance.props = newProps;
  instance.state = newState;
  instance.context = nextContext;

  // 返回是否需要更
  return shouldUpdate;
}

function processUpdateQueue(workInProgress, props, instance, renderLanes) {
  // 是否子集  NoLane为0是任何数字的子集
  function isSubsetOfLanes(set, subset) {
    return (set & subset) === subset;
  }

  // 从update中获取state
  function getStateFromUpdate(
    workInProgress,
    queue,
    update,
    prevState,
    nextProps,
    instance
  ) {
    switch (update.tag) {
      // ...
      case UpdateState: {
        // 更新状态 进入这里
        const payload = update.payload;
        let partialState;
        // 如果update是一个方法 例如this.setState((state, props) => (newState)) 调用方法返回新的state
        if (typeof payload === "function") {
          partialState = payload.call(instance, prevState, nextProps);
        } else {
          // 部分状态对象
          partialState = payload;
        }
        if (partialState === null || partialState === undefined) {
          // 如果得到的状态为空 视为无操作 返回上一个state
          return prevState;
        }
        // 合并上一个状态和新得到的部分状态 浅层合并
        return Object.assign({}, prevState, partialState);
      }
      // ...
    }
    return prevState;
  }

  // 全局变量workInProgressRootSkippedLanes合并传入的lane
  function markSkippedUpdateLanes(lane) {
    workInProgressRootSkippedLanes = mergeLanes(
      lane,
      workInProgressRootSkippedLanes
    );
  }

  // 这在ClassComponent或HostRoot上总是非空。
  const queue = workInProgress.updateQueue;

  hasForceUpdate = false;

  let firstBaseUpdate = queue.firstBaseUpdate;
  let lastBaseUpdate = queue.lastBaseUpdate;

  // 检查是否有等待更新。如果有,请将其转移到基础队列中。
  let pendingQueue = queue.shared.pending;
  // true
  if (pendingQueue !== null) {
    queue.shared.pending = null;

    // 挂起的队列是循环的。断开第一个和最后一个之间的指针,使其非循环。
    const lastPendingUpdate = pendingQueue;
    const firstPendingUpdate = lastPendingUpdate.next;
    lastPendingUpdate.next = null;
    // 将待定更新添加到基本队列中
    if (lastBaseUpdate === null) {
      firstBaseUpdate = firstPendingUpdate;
    } else {
      lastBaseUpdate.next = firstPendingUpdate;
    }
    lastBaseUpdate = lastPendingUpdate;

    // 如果当前有一个队列,而且它与基础队列不同,那么我们也需要将更新的内容转移到这个队列中。
    // 因为基本队列是一个没有周期的单链路链表,所以我们可以附加到两个链表中,并利用结构共享的优势。
    const current = workInProgress.alternate;
    if (current !== null) {
      // 这在ClassComponent或HostRoot上总是非空。
      const currentQueue = current.updateQueue;
      const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
      if (currentLastBaseUpdate !== lastBaseUpdate) {
        if (currentLastBaseUpdate === null) {
          currentQueue.firstBaseUpdate = firstPendingUpdate;
        } else {
          currentLastBaseUpdate.next = firstPendingUpdate;
        }
        currentQueue.lastBaseUpdate = lastPendingUpdate;
      }
    }
  }

  // 这些值可能会随着我们处理队列而改变。
  // true
  if (firstBaseUpdate !== null) {
    let newState = queue.baseState;
    let newLanes = NoLanes;

    let newBaseState = null;
    let newFirstBaseUpdate = null;
    let newLastBaseUpdate = null;

    let update = firstBaseUpdate;
    do {
      const updateLane = update.lane;
      const updateEventTime = update.eventTime;
      if (!isSubsetOfLanes(renderLanes, updateLane)) {
        // 优先权不足。跳过此更新。如果这是第一个
        // 跳过更新,以前的更新/状态是新的基础更新/状态。
        const clone = {
          eventTime: updateEventTime,
          lane: updateLane,
          suspenseConfig: update.suspenseConfig,

          tag: update.tag,
          payload: update.payload,
          callback: update.callback,

          next: null,
        };
        if (newLastBaseUpdate === null) {
          newFirstBaseUpdate = newLastBaseUpdate = clone;
          newBaseState = newState;
        } else {
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }
        // 更新队列中的剩余优先级。
        newLanes = mergeLanes(newLanes, updateLane);
      } else {
        // true
        // 此更新确实具有足够的优先级。
        if (newLastBaseUpdate !== null) {
          // false
          const clone = {
            eventTime: updateEventTime,
            // 此更新将要提交,因此我们永远都不想取消提交
            // 它。使用NoLane是可行的,因为0是所有位掩码的子集,因此
            // 这将永远不会被上面的检查跳过。
            lane: NoLane,
            suspenseConfig: update.suspenseConfig,

            tag: update.tag,
            payload: update.payload,
            callback: update.callback,

            next: null,
          };
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }

        // 获取最新的state
        newState = getStateFromUpdate(
          workInProgress,
          queue,
          update,
          newState,
          props,
          instance
        );
        const callback = update.callback;
        if (callback !== null) {
          workInProgress.effectTag |= Callback;
          const effects = queue.effects;
          if (effects === null) {
            queue.effects = [update];
          } else {
            effects.push(update);
          }
        }
      }
      update = update.next;
      if (update === null) {
        pendingQueue = queue.shared.pending;
        if (pendingQueue === null) {
          // 退出while循环
          break;
        } else {
          // 暂时不清楚什么时候进入这里

          // 一个从reducer内部安排了一次更新
          // 新增新的待处理的更新链表末尾并继续处理。
          const lastPendingUpdate = pendingQueue;
          // 故意不健全。待更新形成了一个循环链表,但我们
          // 在转移到基本队列时,解开它们。
          const firstPendingUpdate = lastPendingUpdate.next;
          lastPendingUpdate.next = null;
          update = firstPendingUpdate;
          queue.lastBaseUpdate = lastPendingUpdate;
          queue.shared.pending = null;
        }
      }
    } while (true);

    if (newLastBaseUpdate === null) {
      // true
      newBaseState = newState;
    }

    queue.baseState = newBaseState;
    queue.firstBaseUpdate = newFirstBaseUpdate; // null
    queue.lastBaseUpdate = newLastBaseUpdate; // null

    markSkippedUpdateLanes(newLanes); // workInProgressRootSkippedLanes = 0
    workInProgress.lanes = newLanes; // 0
    workInProgress.memoizedState = newState; // 将新的state从设置成memoizedState
  }
}

// 检查是否需要更新
function checkShouldComponentUpdate(
  workInProgress,
  ctor,
  oldProps,
  newProps,
  oldState,
  newState,
  nextContext
) {
  const instance = workInProgress.stateNode;
  // 如果实例有shouldComponentUpdate调用判断是否需要更新
  if (typeof instance.shouldComponentUpdate === "function") {
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextContext
    );

    return shouldUpdate;
  }

  // 如果是PureComponent则对props和state进行浅比较
  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    );
  }

  // 如果是普通的Component并且没有shouldComponentUpdate方法
  // 默认为要更新
  return true;
}

// PureComponent的浅比较实现
function shallowEqual(objA, objB) {
  function is(x, y) {
    return (
      (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
    );
  }

  const hasOwnProperty = Object.prototype.hasOwnProperty;

  // 地址比较,如果地址相同不需要更新
  if (is(objA, objB)) {
    return true;
  }

  // 如果有其中一个对象为非object或者为空,需要更新
  if (
    typeof objA !== "object" ||
    objA === null ||
    typeof objB !== "object" ||
    objB === null
  ) {
    return false;
  }

  // 获取新旧对象的属性名
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  // 如果属性的个数不相同,需要更新
  if (keysA.length !== keysB.length) {
    return false;
  }

  // 如果不包含同一属性,或者有一个属性并非相同地址,需要更新
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  // 其他情况不需要更新
  return true;
}

function finishClassComponent(
  current,
  workInProgress,
  Component,
  shouldUpdate,
  hasContext,
  renderLanes
) {
  function markRef(current, workInProgress) {
    const ref = workInProgress.ref;
    if (
      (current === null && ref !== null) ||
      (current !== null && current.ref !== ref)
    ) {
      workInProgress.effectTag |= Ref;
    }
  }

  // 判断是否有使用或者是否需要更新ref,标记effectTag,在之后处理副作用中用到
  markRef(current, workInProgress);

  const didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;

  // shouldUpdate为true不进入
  if (!shouldUpdate && !didCaptureError) {
    if (hasContext) {
      invalidateContextProvider(workInProgress, Component, false);
    }

    // 如果shouldUpdate为flase 从当前树浅拷贝一份节点
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }

  // 类组件实例
  const instance = workInProgress.stateNode;

  // Rerender
  ReactCurrentOwner.current = workInProgress;
  let nextChildren;
  if (
    didCaptureError &&
    typeof Component.getDerivedStateFromError !== "function"
  ) {
    nextChildren = null;

    if (enableProfilerTimer) {
      stopProfilerTimerIfRunning(workInProgress);
    }
  } else {
    // 重新调用实例render方法获得新的children
    nextChildren = instance.render();
  }

  // 标记effectTag PerformedWork
  workInProgress.effectTag |= PerformedWork;
  if (current !== null && didCaptureError) {
    forceUnmountCurrentAndReconcile(
      current,
      workInProgress,
      nextChildren,
      renderLanes
    );
  } else {
    // 处理完了当前节点,调节子节点
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  }

  // 更新当前树节点的memoizedState
  workInProgress.memoizedState = instance.state;

  if (hasContext) {
    invalidateContextProvider(workInProgress, Component, true);
  }

  // 返回调节后的子节点,当前步骤结束
  return workInProgress.child;
}

function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
  if (current === null) {
    // 初次创建时当前树的各个子节点为空才会进入这里
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderLanes
    );
  } else {
    // setState在第一次创建之后都进入此处
    workInProgress.child = reconcileChildFibers(
      workInProgress,
      current.child, // 巨坑!注意是当点树的节点的子节点
      nextChildren,
      renderLanes
    );
  }
}

function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
  const isObject = typeof newChild === "object" && newChild !== null;
  if (isObject) {
    switch (newChild.?typeof) {
      // ...
      case REACT_ELEMENT_TYPE:
        return placeSingleChild(
          reconcileSingleElement(
            returnFiber,
            currentFirstChild,
            newChild,
            lanes
          )
        );
      default:
        break;
    }
  }
}

function placeSingleChild(newFiber) {
  // 标记fiber节点需要防止新的子节点
  if (shouldTrackSideEffects && newFiber.alternate === null) {
    newFiber.effectTag = Placement;
  }
  return newFiber;
}

function reconcileSingleElement(
  returnFiber,
  currentFirstChild,
  element,
  lanes
) {
  const key = element.key;
  let child = currentFirstChild;

  while (child !== null) {
    // 比较key
    if (child.key === key) {
      switch (child.tag) {
        // ...
        default: {
          // 如果子节点保留同样的组件类型
          if (child.elementType === element.type) {
            // 删除当前树中的其他的同级节点,如果当前节点同级没有别的节点则不处理
            deleteRemainingChildren(returnFiber, child.sibling);
            // 从当前树中拷贝子节点到工作树
            const existing = useFiber(child, element.props);
            existing.ref = coerceRef(returnFiber, child, element);
            // 设置该节点的父节点
            existing.return = returnFiber;
            return existing;
          }
          break;
        }
      }
      // 删除所有的同级节点
      deleteRemainingChildren(returnFiber, child);
      break;
    } else {
      // 如果key不相同直接删除
      deleteChild(returnFiber, child);
    }
    child = child.sibling;
  }

  if (element.type === REACT_FRAGMENT_TYPE) {
    // ...
  } else {
    // 重新创造fiber节点
    const created = createFiberFromElement(element, returnFiber.mode, lanes);
    created.ref = coerceRef(returnFiber, currentFirstChild, element);
    created.return = returnFiber;
    return created;
  }
}

// 删除节点
function deleteChild(returnFiber, childToDelete) {
  if (!shouldTrackSideEffects) {
    return;
  }
  const last = returnFiber.lastEffect;
  if (last !== null) {
    last.nextEffect = childToDelete;
    returnFiber.lastEffect = childToDelete;
  } else {
    returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
  }
  childToDelete.nextEffect = null;
  childToDelete.effectTag = Deletion;
}

// 删除同级下所有节点
function deleteRemainingChildren(returnFiber, currentFirstChild) {
  if (!shouldTrackSideEffects) {
    return null;
  }

  let childToDelete = currentFirstChild;

  while (childToDelete !== null) {
    deleteChild(returnFiber, childToDelete);
    childToDelete = childToDelete.sibling;
  }
  return null;
}

// 克隆节点
function useFiber(fiber, pendingProps) {
  const clone = createWorkInProgress(fiber, pendingProps);
  clone.index = 0;
  clone.sibling = null;
  return clone;
}

// ref相关
function coerceRef(returnFiber, current, element) {
  const mixedRef = element.ref;
  if (
    mixedRef !== null &&
    typeof mixedRef !== "function" &&
    typeof mixedRef !== "object"
  ) {
    if (element._owner) {
      const owner = element._owner;
      let inst;
      if (owner) {
        const ownerFiber = owner;
        inst = ownerFiber.stateNode;
      }
      const stringRef = "" + mixedRef;
      if (
        current !== null &&
        current.ref !== null &&
        typeof current.ref === "function" &&
        current.ref._stringRef === stringRef
      ) {
        return current.ref;
      }
      const ref = function (value) {
        let refs = inst.refs;
        if (refs === emptyRefsObject) {
          refs = inst.refs = {};
        }
        if (value === null) {
          delete refs[stringRef];
        } else {
          refs[stringRef] = value;
        }
      };
      ref._stringRef = stringRef;
      return ref;
    } else {
    }
  }
  return mixedRef;
}

// 根据element创建fiber节点
function createFiberFromElement(element, mode, lanes) {
  let owner = null;
  const type = element.type;
  const key = element.key;
  const pendingProps = element.props;
  const fiber = createFiberFromTypeAndProps(
    type,
    key,
    pendingProps,
    owner,
    mode,
    lanes
  );
  return fiber;
}

function createFiberFromTypeAndProps(
  type, // React$ElementType
  key,
  pendingProps,
  owner,
  mode,
  lanes
) {
  let fiberTag = IndeterminateComponent;
  // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
  let resolvedType = type;
  if (typeof type === "function") {
    if (shouldConstruct(type)) {
      fiberTag = ClassComponent;
    }
  } else if (typeof type === "string") {
    fiberTag = HostComponent;
  } else {
    getTag: switch (
      type
      // ...
    ) {
    }
  }

  const fiber = createFiber(fiberTag, pendingProps, key, mode);
  fiber.elementType = type;
  fiber.type = resolvedType;
  fiber.lanes = lanes;

  return fiber;
}

5.3 更新需要更新的 HostComponent 节点

通过上面四种策略构建出了完整的新的 Fiber 树

function beginWork(current, workInProgress, renderLanes) {
  const updateLanes = workInProgress.lanes;

  if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      // 5.2中更新了props,只进入这里
      didReceiveUpdate = true;
    } else if (!includesSomeLane(renderLanes, updateLanes)) {
      didReceiveUpdate = false;
    } else {
      didReceiveUpdate = false;
    }
  }

  switch (workInProgress.tag) {
    // ...
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderLanes);
    // ...
  }
}

function updateHostComponent(current, workInProgress, renderLanes) {
  // context相关不了解
  pushHostContext(workInProgress);

  // 当前树节点不为空
  if (current === null) {
    // false
    tryToClaimNextHydratableInstance(workInProgress);
  }

  const type = workInProgress.type;
  const nextProps = workInProgress.pendingProps;
  const prevProps = current !== null ? current.memoizedProps : null;

  let nextChildren = nextProps.children;

  // 是否文本节点
  const isDirectTextChild = shouldSetTextContent(type, nextProps);

  if (isDirectTextChild) {
    // 如果是文本节点就不需要考虑处理它的子节点了
    nextChildren = null;
  } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
    // If we're switching from a direct text child to a normal child, or to
    // empty, we need to schedule the text content to be reset.
    workInProgress.effectTag |= ContentReset;
  }

  markRef(current, workInProgress);

  // ConcurrentMode模式的一些处理
  if (
    (workInProgress.mode & ConcurrentMode) !== NoMode &&
    nextProps.hasOwnProperty("hidden")
  ) {
    const wrappedChildren = {
      ?typeof: REACT_ELEMENT_TYPE,
      type: REACT_LEGACY_HIDDEN_TYPE,
      key: null,
      ref: null,
      props: {
        children: nextChildren,
        // Check the host config to see if the children are offscreen/hidden.
        mode: shouldDeprioritizeSubtree(type, nextProps) ? "hidden" : "visible",
      },
      _owner: __DEV__ ? {} : null,
    };
    nextChildren = wrappedChildren;
  }

  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

function shouldSetTextContent(type, props) {
  return (
    type === "textarea" ||
    type === "option" ||
    type === "noscript" ||
    typeof props.children === "string" ||
    typeof props.children === "number" ||
    (typeof props.dangerouslySetInnerHTML === "object" &&
      props.dangerouslySetInnerHTML !== null &&
      props.dangerouslySetInnerHTML.__html != null)
  );
}

6 diff 每个节点生成更新副作用

在之前的 4-5 步中我们构建了一颗新的工作树,并且为目标更新节点的子节点们做了 reconcile(调节),它们或许改变了组件类型,又或者有了新的 props(props 可能只是地址改变了但是内容其实还是一直的),在React 初次渲染源码剖析文中会讲到当 workLoop 遍历完所有子节点后会由 completeUnitOfWork 方法向上遍历,真正的 diff 实际上是在这个方法里完成的。
如果工作树节点有对应的当前树节点就会进行 diff,diff 前树的 memoizedProps 和工作树的 pendingProps 产生一个差异数据 updatePayload,将其挂载在对应的工作树节点的 updateQueue 下,标记该节点 effectTag 需要更新,并且将改节点置入副作用链表中,在下一步根据副作用链表去应用更新
在 diff 过程时会对新旧属性值做比较,如果相同则会跳过比较
updatePayload 数据结构为普通的数组,数组下表为奇数的是 keyName,偶数是 keyValue,即:["children", "new text"]
此处包含的策略: ① 只对在当前树有对应节点的工作树节点进行 diff,无工作树对应节则创建新的节点

function completeUnitOfWork(unitOfWork) {
  let completedWork = unitOfWork;

  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;

    if ((completedWork.effectTag & Incomplete) === NoEffect) {
      let next;
      if (
        !enableProfilerTimer ||
        (completedWork.mode & ProfileMode) === NoMode
      ) {
        next = completeWork(current, completedWork, subtreeRenderLanes);
      } else {
        next = completeWork(current, completedWork, subtreeRenderLanes);
      }

      resetChildLanes(completedWork);

      if (next !== null) {
        workInProgress = next;
        return;
      }

      if (
        returnFiber !== null &&
        (returnFiber.effectTag & Incomplete) === NoEffect
      ) {
        if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = completedWork.firstEffect;
        }
        if (completedWork.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
          }
          returnFiber.lastEffect = completedWork.lastEffect;
        }

        const effectTag = completedWork.effectTag;

        if (effectTag > PerformedWork) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = completedWork;
          } else {
            returnFiber.firstEffect = completedWork;
          }
          returnFiber.lastEffect = completedWork;
        }
      }
    } else {
      // ...
    }

    const siblingFiber = completedWork.sibling;

    // 如果有同级节点,先处理同级
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }
    // 没有同级节点,处理父节点
    completedWork = returnFiber;
    workInProgress = completedWork;
  } while (completedWork !== null);

  if (workInProgressRootExitStatus === RootIncomplete) {
    workInProgressRootExitStatus = RootCompleted;
  }
}

function completeWork(current, workInProgress, renderLanes) {
  const newProps = workInProgress.pendingProps;

  switch (workInProgress.tag) {
    // ...
    case HostComponent: {
      popHostContext(workInProgress);
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      // 如果有对应的当前树节点 则进行diff从而实现更新
      if (current !== null && workInProgress.stateNode != null) {
        // 更新HostComponent类型组件
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance
        );

        if (current.ref !== workInProgress.ref) {
          markRef(workInProgress);
        }
      } else {
        // 如果没有对应的当前树节点 不需要diff 执行创建操作 代码省略
        // ...
      }
      return null;
    }
  }
}

function updateHostComponent(
  current,
  workInProgress,
  type,
  newProps,
  rootContainerInstance
) {
  // 旧的props
  const oldProps = current.memoizedProps;
  // 如果新旧props相同,则不需要更新
  if (oldProps === newProps) {
    return;
  }

  // 组件实例,通常对应真实的DOM节点
  const instance = workInProgress.stateNode;
  const currentHostContext = getHostContext();

  // 准备更新数据
  const updatePayload = prepareUpdate(
    instance,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
    currentHostContext
  );
  // 准备更新返回的更新数据置入工作树节点的updateQueue中
  workInProgress.updateQueue = updatePayload;
  // 如果更新数据不为空,标记effectTag为更新状态,在一步中处理副作用
  if (updatePayload) {
    markUpdate(workInProgress);
  }
}

function prepareUpdate(
  domElement,
  type,
  oldProps,
  newProps,
  rootContainerInstance,
  hostContext
) {
  return diffProperties(
    domElement,
    type,
    oldProps,
    newProps,
    rootContainerInstance
  );
}

// diff出payload
function diffProperties(
  domElement,
  tag,
  lastRawProps,
  nextRawProps,
  rootContainerElement
) {
  const DANGEROUSLY_SET_INNER_HTML = "dangerouslySetInnerHTML";
  const SUPPRESS_CONTENT_EDITABLE_WARNING = "suppressContentEditableWarning";
  const SUPPRESS_HYDRATION_WARNING = "suppressHydrationWarning";
  const AUTOFOCUS = "autoFocus";
  const CHILDREN = "children";
  const STYLE = "style";
  const HTML = "__html";
  const DEPRECATED_flareListeners = "DEPRECATED_flareListeners";

  const registrationNameModules = {};

  let updatePayload = null;

  let lastProps;
  let nextProps;
  switch (tag) {
    // ...
    default: {
      lastProps = lastRawProps;
      nextProps = nextRawProps;
      break;
    }
  }

  assertValidProps(tag, nextProps);

  let propKey;
  let styleName;
  let styleUpdates = null;
  // 处理旧的props
  for (propKey in lastProps) {
    // 如果新的props有旧的props没有的属性跳过
    if (
      nextProps.hasOwnProperty(propKey) ||
      !lastProps.hasOwnProperty(propKey) ||
      lastProps[propKey] == null
    ) {
      continue;
    }
    if (propKey === STYLE) {
      const lastStyle = lastProps[propKey];
      for (styleName in lastStyle) {
        if (lastStyle.hasOwnProperty(styleName)) {
          if (!styleUpdates) {
            styleUpdates = {};
          }
          styleUpdates[styleName] = "";
        }
      }
    } else if (propKey === DANGEROUSLY_SET_INNER_HTML || propKey === CHILDREN) {
    } else if (
      (enableDeprecatedFlareAPI && propKey === DEPRECATED_flareListeners) ||
      propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
      propKey === SUPPRESS_HYDRATION_WARNING
    ) {
    } else if (propKey === AUTOFOCUS) {
    } else if (registrationNameModules.hasOwnProperty(propKey)) {
      if (!updatePayload) {
        updatePayload = [];
      }
    } else {
      (updatePayload = updatePayload || []).push(propKey, null);
    }
  }
  // 处理新的props
  for (propKey in nextProps) {
    // 新的属性值
    const nextProp = nextProps[propKey];
    // 旧的属性值
    const lastProp = lastProps != null ? lastProps[propKey] : undefined;
    // 如果新旧属性值相同跳过
    if (
      !nextProps.hasOwnProperty(propKey) ||
      nextProp === lastProp ||
      (nextProp == null && lastProp == null)
    ) {
      continue;
    }
    if (propKey === STYLE) {
      if (lastProp) {
        // Unset styles on `lastProp` but not on `nextProp`.
        for (styleName in lastProp) {
          if (
            lastProp.hasOwnProperty(styleName) &&
            (!nextProp || !nextProp.hasOwnProperty(styleName))
          ) {
            if (!styleUpdates) {
              styleUpdates = {};
            }
            styleUpdates[styleName] = "";
          }
        }
        // Update styles that changed since `lastProp`.
        for (styleName in nextProp) {
          if (
            nextProp.hasOwnProperty(styleName) &&
            lastProp[styleName] !== nextProp[styleName]
          ) {
            if (!styleUpdates) {
              styleUpdates = {};
            }
            styleUpdates[styleName] = nextProp[styleName];
          }
        }
      } else {
        // Relies on `updateStylesByID` not mutating `styleUpdates`.
        if (!styleUpdates) {
          if (!updatePayload) {
            updatePayload = [];
          }
          updatePayload.push(propKey, styleUpdates);
        }
        styleUpdates = nextProp;
      }
    } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
      const nextHtml = nextProp ? nextProp[HTML] : undefined;
      const lastHtml = lastProp ? lastProp[HTML] : undefined;
      if (nextHtml != null) {
        if (lastHtml !== nextHtml) {
          (updatePayload = updatePayload || []).push(propKey, nextHtml);
        }
      } else {
        // TODO: It might be too late to clear this if we have children
        // inserted already.
      }
    } else if (propKey === CHILDREN) {
      if (typeof nextProp === "string" || typeof nextProp === "number") {
        (updatePayload = updatePayload || []).push(propKey, "" + nextProp);
      }
    } else if (
      (enableDeprecatedFlareAPI && propKey === DEPRECATED_flareListeners) ||
      propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
      propKey === SUPPRESS_HYDRATION_WARNING
    ) {
      // Noop
    } else if (registrationNameModules.hasOwnProperty(propKey)) {
      if (nextProp != null) {
        // We eagerly listen to this even though we haven't committed yet.
        if (__DEV__ && typeof nextProp !== "function") {
          warnForInvalidEventListener(propKey, nextProp);
        }
        ensureListeningTo(rootContainerElement, propKey);
      }
      if (!updatePayload && lastProp !== nextProp) {
        // This is a special case. If any listener updates we need to ensure
        // that the "current" props pointer gets updated so we need a commit
        // to update this element.
        updatePayload = [];
      }
    } else if (
      typeof nextProp === "object" &&
      nextProp !== null &&
      nextProp.?typeof === REACT_OPAQUE_ID_TYPE
    ) {
      // If we encounter useOpaqueReference's opaque object, this means we are hydrating.
      // In this case, call the opaque object's toString function which generates a new client
      // ID so client and server IDs match and throws to rerender.
      nextProp.toString();
    } else {
      // For any other property we always add it to the queue and then we
      // filter it out using the whitelist during the commit.
      (updatePayload = updatePayload || []).push(propKey, nextProp);
    }
  }
  if (styleUpdates) {
    if (__DEV__) {
      validateShorthandPropertyCollisionInDev(styleUpdates, nextProps[STYLE]);
    }
    (updatePayload = updatePayload || []).push(STYLE, styleUpdates);
  }
  return updatePayload;
}

function markUpdate(workInProgress) {
  workInProgress.effectTag |= Update;
}

7 应用更新数据

在第 6 步中产生了 effectTag 被标记为 update 并且已经 diff 出来差异数据 updatePayload 的 fiber 节点,这些节点都被以从下至上的顺序关联进副作用链表,这一步也是整个更新过程的最后一步,功能就是应用更新数据(updatePayload)去操作真实的 DOM 节点,最后把工作树切换成为当前树。

function commitRootImpl(root, renderPriorityLevel) {
  // 完整的工作树
  const finishedWork = root.finishedWork;

  let firstEffect;
  if (finishedWork.effectTag > PerformedWork) {
    if (finishedWork.lastEffect !== null) {
      finishedWork.lastEffect.nextEffect = finishedWork;
      firstEffect = finishedWork.firstEffect;
    } else {
      firstEffect = finishedWork;
    }
  } else {
    firstEffect = finishedWork.firstEffect;
  }

  if (firstEffect !== null) {
    // ...
    nextEffect = firstEffect;
    do {
      try {
        // 应用更新 操作真实DOM
        commitMutationEffects(root, renderPriorityLevel);
      } catch (error) {
        invariant(nextEffect !== null, "Should be working on an effect.");
        captureCommitPhaseError(nextEffect, error);
        nextEffect = nextEffect.nextEffect;
      }
    } while (nextEffect !== null);

    // 切换工作树为当前树
    root.current = finishedWork;

    // ...
  }

  // ...
}

function commitMutationEffects(root, renderPriorityLevel) {
  while (nextEffect !== null) {
    const effectTag = nextEffect.effectTag;

    const primaryEffectTag =
      effectTag & (Placement | Update | Deletion | Hydrating);
    switch (primaryEffectTag) {
      // ...
      case Update: {
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      case Deletion: {
        commitDeletion(root, nextEffect, renderPriorityLevel);
        break;
      }
    }
    nextEffect = nextEffect.nextEffect;
  }
}

function commitWork(current, finishedWork) {
  // ...

  switch (finishedWork.tag) {
    // ...
    case HostComponent: {
      // 真实DOM实例
      const instance = finishedWork.stateNode;
      if (instance != null) {
        // 新props
        const newProps = finishedWork.memoizedProps;
        // 旧props
        const oldProps = current !== null ? current.memoizedProps : newProps;
        // 组件类型
        const type = finishedWork.type;
        // 更新数据
        const updatePayload = finishedWork.updateQueue;
        finishedWork.updateQueue = null;
        // 处理更新数据updatePayload
        if (updatePayload !== null) {
          commitUpdate(
            instance,
            updatePayload,
            type,
            oldProps,
            newProps,
            finishedWork
          );
        }
        // ...
      }
      return;
    }
    // ...
  }
}

function commitUpdate(
  domElement,
  updatePayload,
  type,
  oldProps,
  newProps,
  internalInstanceHandle
) {
  // DOM属性关联新props
  updateFiberProps(domElement, newProps);
  // 应用更新数据到真实DOM节点
  updateProperties(domElement, updatePayload, type, oldProps, newProps);
}

function updateFiberProps(node, props) {
  const internalPropsKey = "__reactProps$" + randomKey;
  node[internalPropsKey] = props;
}

function updateProperties(
  domElement,
  updatePayload,
  tag,
  lastRawProps,
  nextRawProps
) {
  if (
    tag === "input" &&
    nextRawProps.type === "radio" &&
    nextRawProps.name != null
  ) {
    ReactDOMInputUpdateChecked(domElement, nextRawProps);
  }

  // 不理会
  const wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
  const isCustomComponentTag = isCustomComponent(tag, nextRawProps);
  // 应用diff.
  updateDOMProperties(
    domElement,
    updatePayload,
    wasCustomComponentTag,
    isCustomComponentTag
  );

  switch (tag) {
    case "input":
      ReactDOMInputUpdateWrapper(domElement, nextRawProps);
      break;
    case "textarea":
      ReactDOMTextareaUpdateWrapper(domElement, nextRawProps);
      break;
    case "select":
      ReactDOMSelectPostUpdateWrapper(domElement, nextRawProps);
      break;
  }
}

function updateDOMProperties(
  domElement,
  updatePayload,
  wasCustomComponentTag,
  isCustomComponentTag
) {
  for (let i = 0; i < updatePayload.length; i += 2) {
    const propKey = updatePayload[i];
    const propValue = updatePayload[i + 1];
    if (propKey === STYLE) {
      // 更新样式
      setValueForStyles(domElement, propValue);
    } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
      // 更新InnerHTML
      setInnerHTML(domElement, propValue);
    } else if (propKey === CHILDREN) {
      // 更新内容
      setTextContent(domElement, propValue);
    } else {
      // 设置属性
      setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
    }
  }
}

function setTextContent(node, text) {
  if (text) {
    const firstChild = node.firstChild;

    if (
      firstChild &&
      firstChild === node.lastChild &&
      firstChild.nodeType === TEXT_NODE
    ) {
      firstChild.nodeValue = text;
      return;
    }
  }
  node.textContent = text;
}

8 总结

react 总是会且仅有两颗树,一颗是根据当前状态构建的当前树,另一颗是根据下一个状态构建的工作树,diff 就是通过这两颗树下的每个节点的新旧 props 属性去实现的,最终会 diff 出一个 updatePayload 我称之为更新数据包含详细的需要更新的内容,将需要更新的节点用链表形式关联起来,形成一个副作用链表,然后在 commit 阶段应用更新内容,最终把工作树切换成为当前树,作为下一次更新的参照树。
包含的策略:
① 目标更新的节点(包括自己)和它的所有父节点,在当前树中浅拷贝一份节点(防止修改一棵树的节点影响到另一棵树的节点)
② 目标更新的节点的子节点,重新 reconcile(调节),包括重新设置节点类型或者设置新的 pendingProps 如果是类组件还要调用 update 相关生命。
③ 比较新生成的 element 和当前树节点的 key,如果不相同直接删除不进入策略 ④
④ 如果组件类型相同,希望对应两颗子树有相同的结构,所以直接从当前树中拷贝一份,反之根据组件重新创建对应的 fiber 节点
⑤ 只对在当前树有对应节点的工作树节点进行 diff,无工作树对应节则创建新的节点

总结:setState、fourceUpdate 这类更新操作每次都需要构建一颗新的工作树,即使传入参数为空或者与之前的 state 一致,在复杂的项目中 fiber 节点的数量是非常可怕的即使 react diff 的时间复杂度为 O(n),ClassComponent 的子组件越多 setState 的性能消耗就越大,因为触发 setState 组件的子组件的所有节点都会触发更新和进行 diff,针对这一点 react 也有进行相关的优化,但是看了大量的文章只讲了优化的细节却没有讲如何开启优化。

react 的三种模式

react modes

开启方法:

Migration Step

不要再问 react setState 是同步还是异步的问题了,抛开渲染模式讲没有意义,在传统模式也就是我们最常用的 React.render 下所有 setState 都是同步的,只有用到最新的并行模式(Concurrent Mode)setState 才可能是异步的,传统模式与 Blocking Model 的最重要的优化就是自动合并多个 setState(Automatic batching of multiple setStates),会将多个 setState 合并成为一个从而不需要构建多遍工作树简称 batching。

下一篇将异步的 setState 和 batching 源码实现