React19源码系列之渲染阶段performUnitOfWork

在 React 内部实现中,将 render 函数分为两个阶段:

  1. 渲染阶段
  2. 提交阶段

其中渲染阶段可以分为 beginWork 和 completeWork 两个阶段,而提交阶段对应着 commitWork。

在之前的root.render过程中,渲染过程无论是并发模式执行还是同步模式执行,都是执行到函数performUnitOfWork,所以,这次,具体看看performUnitOfWork函数的执行过程。

workLoopSync

performUnitOfWork

performUnitOfWork 函数的主要作用是执行单个 Fiber 节点的工作单元。该函数会根据当前 Fiber 节点的状态,调用 beginWork 函数开始处理这个 Fiber 节点,并根据处理结果决定是继续处理下一个 Fiber 节点,还是完成当前 Fiber 节点的工作。

函数参数含义

  • unitOfWork:类型为 Fiber,表示当前要处理的 Fiber 节点,也就是当前的工作单元。
javascript 复制代码
function performUnitOfWork(unitOfWork: Fiber): void {
  // unitOfWork.alternate 指向当前 Fiber 节点的替代节点。在 React 的协调过程中,每个 Fiber 节点都有一个对应的替代节点,用于在双缓冲机制中存储旧的状态。这里将其赋值给 current。
  const current = unitOfWork.alternate;

  // next:用于存储 beginWork 函数返回的下一个要处理的 Fiber 节点。
  let next;


// 调用 beginWork 函数开始处理当前 Fiber 节点,传入当前 Fiber 节点的替代节点 current、当前 Fiber 节点 unitOfWork 以及相关的渲染车道 entangledRenderLanes。beginWork 函数会根据 Fiber 节点的类型和状态,执行相应的工作,如创建新的 Fiber 节点、更新已有 Fiber 节点等,并返回下一个要处理的 Fiber 节点。
  next = beginWork(current, unitOfWork, entangledRenderLanes);
  

  // 将 unitOfWork 的 pendingProps(待处理的属性)赋值给 memoizedProps(记忆化的属性)。这意味着当前 Fiber 节点的属性已经被处理并确定下来。
  unitOfWork.memoizedProps = unitOfWork.pendingProps;

  // 如果 next 为 null,说明 beginWork 函数没有返回下一个要处理的 Fiber 节点,即当前 Fiber 节点的工作没有产生新的工作。
  if (next === null) {
    // If this doesn't spawn new work, complete the current work.
    // 调用 completeUnitOfWork 函数完成当前 Fiber 节点的工作,可能会进行一些清理和提交操作。
    completeUnitOfWork(unitOfWork);
  } else {
    // 如果 next 不为 null,将 next 赋值给 workInProgress,表示下一个要处理的 Fiber 节点成为当前的工作进行中的节点,后续将继续处理这个节点。
    workInProgress = next;
  }
}

渲染阶段一beginWork

beginWork 函数的主要任务是根据不同的 Fiber 节点类型和更新情况,对当前 Fiber 节点进行处理,决定是复用旧的 Fiber 节点,还是创建新的子 Fiber 节点。该函数会根据 Fiber 节点的 tag 属性(表示节点类型),调用不同的处理函数来更新或挂载节点。

beginWork 函数接收三个参数:

  • current:旧的 Fiber 节点,若为 null 则表示没有旧节点。
  • workInProgress:新的 Fiber 节点,也就是要进行处理的节点。
  • renderLanes:渲染优先级车道,用于标识当前渲染任务的优先级。
javascript 复制代码
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {

  // 有旧节点,非首次渲染
  if (current !== null) {
    // current.memoizedProps 存储了旧 Fiber 节点上一次渲染时使用的属性。
    const oldProps = current.memoizedProps;

    // workInProgress.pendingProps 存储了新 Fiber 节点即将使用的属性。
    const newProps = workInProgress.pendingProps;

    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() )
    ) {

      didReceiveUpdate = true;
      
    } else {
      
      const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
        current,
        renderLanes,
      );

      // 尝试提前退出渲染流程
      if (
        !hasScheduledUpdateOrContext &&
        (workInProgress.flags & DidCapture) === NoFlags
      ) {
        // No pending updates or context. Bail out now.
        didReceiveUpdate = false;
        return attemptEarlyBailoutIfNoScheduledUpdate(
          current,
          workInProgress,
          renderLanes,
        );
      }

      if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
        didReceiveUpdate = true;
      } else {
        didReceiveUpdate = false;
      }
    }
    
  } else {
    
    // 首次渲染时,将 didReceiveUpdate 标记为 false。
    didReceiveUpdate = false;
  }
  
  // 将 workInProgress.lanes 设置为 NoLanes 时,意味着当前 Fiber 节点没有需要处理的更新任务,或者更新任务已经处理完毕。
  // 会清除当前 workInProgress Fiber 节点上的所有更新标记。
  workInProgress.lanes = NoLanes;

  switch (workInProgress.tag) {
      // 省略代码。。。

    case Throw: {
      // This represents a Component that threw in the reconciliation phase.
      // So we'll rethrow here. This might be a Thenable.
      throw workInProgress.pendingProps;
    }
  }


}

<Tag>FunctionComponent(常量为0)

负责解析函数组件的 props,并调用 updateFunctionComponent 执行组件的实际渲染。主要工作包括:

  1. 获取组件类型和待处理的 props。
  2. 解析默认 props(针对非类组件)。
  3. 调用函数组件的更新逻辑。
javascript 复制代码
case FunctionComponent: {
  // fiber的组件类型
  const Component = workInProgress.type;
  // 待处理props
  const unresolvedProps = workInProgress.pendingProps;
  
  const resolvedProps =
    disableDefaultPropsExceptForClasses ||
    workInProgress.elementType === Component
      ? unresolvedProps
      : resolveDefaultPropsOnNonClassComponent(Component, unresolvedProps);
  
  return updateFunctionComponent(
    current,// 表示当前的 Fiber 节点
    workInProgress,// 正在处理的fiber
    Component,// 函数组件
    resolvedProps,// 解析后的属性props
    renderLanes,// 渲染优先级车道
  );
}

updateFunctionComponent

updateFunctionComponent 函数负责执行函数组件,处理 Hooks,并根据返回的 JSX 内容生成新的子 Fiber 节点。该函数实现了函数组件的渲染逻辑、状态管理以及性能优化(如 bailout 机制)。

javascript 复制代码
function updateFunctionComponent(
  current: null | Fiber,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {

  let context;
  if (!disableLegacyContext && !disableLegacyContextForFunctionComponents) {
  //   // 未屏蔽的上下文
    const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
    // 经过屏蔽的上下文
    context = getMaskedContext(workInProgress, unmaskedContext);
  }

  let nextChildren;
  let hasId;
  
  prepareToReadContext(workInProgress, renderLanes);
  
  // 执行hooks
  nextChildren = renderWithHooks(
    current,
    workInProgress,
    Component,
    nextProps,
    context,
    renderLanes,
  );

  // 表示组件没有接收到新的更新。
  if (current !== null && !didReceiveUpdate) {
    // bailoutHooks 会跳过 Hooks 的更新
    bailoutHooks(current, workInProgress, renderLanes);
    // 直接复用之前的结果。
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }

  // React DevTools reads this flag.
  // 标记已完成
  workInProgress.flags |= PerformedWork;
  
  //reconcileChildren 函数用于对比新旧子节点,生成新的 Fiber 树结构。
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  
  return workInProgress.child;
}

工具函数之 resolveDefaultPropsOnNonClassComponent

resolveDefaultPropsOnNonClassComponent 是 React 中用于处理非类组件(如函数组件)默认 props 的函数。它会将组件定义的 defaultProps 对象与实际传入的 props 合并,确保未显式提供的 props 使用默认值。

函数参数含义

  • Component:组件本身(函数或类)。
  • baseProps:实际传入的 props 对象。
javascript 复制代码
function resolveDefaultPropsOnNonClassComponent(
  Component: any,
  baseProps: Object,
): Object {
  // 若启用则仅对类组件保留默认 props 支持,函数组件将完全忽略 defaultProps。
  if (disableDefaultPropsExceptForClasses) {
    // Support for defaultProps is removed in React 19 for all types
    // except classes.
    return baseProps;
  }
  // 合并默认 props
  if (Component && Component.defaultProps) {
    // Resolve default props. Taken from ReactElement
    const props = assign({}, baseProps);
    const defaultProps = Component.defaultProps;
    for (const propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
    return props;
  }
  return baseProps;
}

工具函数之 bailoutHooks

bailoutHooks 函数用于在组件无需重新渲染时跳过 Hooks 的更新。当组件的 props、context 或状态没有变化时,React 会触发 bailout 逻辑,直接复用之前的渲染结果,从而避免不必要的计算和副作用执行。

TypeScript 复制代码
 function bailoutHooks(
  current: Fiber,
  workInProgress: Fiber,
  lanes: Lanes,
): void {
   // 复用 Hooks 更新队列
  workInProgress.updateQueue = current.updateQueue;

   // 清除副作用
   // PassiveEffect:表示存在需要异步执行的副作用(如 useEffect)。
// UpdateEffect:表示存在需要同步执行的副作用(如 useLayoutEffect)。
  workInProgress.flags &= ~(PassiveEffect | UpdateEffect);

   // 从当前 Fiber 的 lanes 中移除本次渲染的优先级,表示该优先级的更新已处理完毕。
  current.lanes = removeLanes(current.lanes, lanes);
}

bailout 机制通常在以下情况触发:

  1. props 未变化 :前后 props 对象通过浅比较(Object.is)相等。
  2. context 未变化:组件依赖的上下文值没有更新。
  3. 被 React.memo 包裹 :函数组件被 React.memo 高阶组件包裹,且 props 未变化。
  4. 无状态更新:组件内部状态没有变化。

工具函数之 bailoutOnAlreadyFinishedWork

bailoutOnAlreadyFinishedWork 函数用于在组件无需重新渲染时直接复用现有 Fiber 树结构。当组件的 props、状态或上下文没有变化时,React 会触发 bailout 逻辑,跳过该组件及其子树的协调过程,直接复用之前的渲染结果,从而显著提升性能。

TypeScript 复制代码
function bailoutOnAlreadyFinishedWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
  if (current !== null) {
    // 直接将当前 Fiber 的依赖信息复制到新 Fiber,避免重新收集依赖。
    workInProgress.dependencies = current.dependencies;
  }

  // 标记跳过的更新优先级
  markSkippedUpdateLanes(workInProgress.lanes);

  // Check if the children have any pending work.
  if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {

    // 启用延迟上下文传播的
    if (enableLazyContextPropagation && current !== null) {

      lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);
      if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
        return null;
      }
    } else {
      return null;
    }
  }

// 克隆子节点
  cloneChildFibers(current, workInProgress);
  return workInProgress.child;
}

工具函数之 markSkippedUpdateLanes

markSkippedUpdateLanes 是 React 性能监控系统中的关键函数,用于记录被跳过的更新优先级。当组件由于 bailout 机制(如 props 未变化)而跳过渲染时,React 使用该函数标记这些被跳过的优先级

javascript 复制代码
function markSkippedUpdateLanes(lane: Lane | Lanes): void {
  workInProgressRootSkippedLanes = mergeLanes(
    lane,
    workInProgressRootSkippedLanes,
  );
}

let workInProgressRootSkippedLanes: Lanes = NoLanes;

工具函数之 includesSomeLane

includesSomeLane 函数用于判断两个优先级集合(Lanes)是否存在交集。

TypeScript 复制代码
function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane): boolean {
  return (a & b) !== NoLanes;
}

工具函数之 cloneChildFibers

cloneChildFibers 函数用于在组件 bailout(跳过渲染)时浅克隆子 Fiber 节点。当父组件因 props 未变化而无需重新渲染时,React 会复用子 Fiber 结构,通过克隆操作创建新的 workInProgress 树,避免重新协调整个子树,从而显著提升性能。

javascript 复制代码
function cloneChildFibers(
  current: Fiber | null,
  workInProgress: Fiber,
): void {
  // 若 current 存在且子节点已变化,抛出错误
  if (current !== null && workInProgress.child !== current.child) {
    throw new Error('Resuming work not yet implemented.');
  }

  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;
  }
  // 最后一个节点的 sibling 设为 null,确保链表正确结束。
  newChild.sibling = null;
}

工具函数之 prepareToReadContext

prepareToReadContext 函数是 React 渲染流程中的关键环节,用于为当前 Fiber 节点准备上下文读取环境。它主要完成以下工作:

  1. 设置当前渲染的 Fiber 节点。
  2. 重置上下文依赖链表。
  3. 处理上下文更新标记,确保依赖的上下文变化能触发组件重新渲染。
javascript 复制代码
function prepareToReadContext(
  workInProgress: Fiber,
  renderLanes: Lanes,
): void {
  // currentlyRenderingFiber:全局变量,指向当前正在渲染的 Fiber 节点。
// lastContextDependency:全局变量,用于构建上下文依赖链表,初始化为 null。
  currentlyRenderingFiber = workInProgress;
  lastContextDependency = null;

  const dependencies = workInProgress.dependencies;
  if (dependencies !== null) {
    if (enableLazyContextPropagation) {
      // Reset the work-in-progress list
      // 重置依赖链表(延迟传播模式)
      dependencies.firstContext = null;
    } else {
      const firstContext = dependencies.firstContext;
      if (firstContext !== null) {
        if (includesSomeLane(dependencies.lanes, renderLanes)) {
          // Context list has a pending update. Mark that this fiber performed work.
              // 上下文有更新,标记当前 Fiber 执行了工作
          markWorkInProgressReceivedUpdate();
        }
        // Reset the work-in-progress list
        dependencies.firstContext = null;
      }
    }
  }
}

<Tag>HostRoot

javascript 复制代码
case HostRoot:
  return updateHostRoot(current, workInProgress, renderLanes);

工具函数之 updateHostRoot

updateHostRoot 是 React 渲染流程中处理根组件(HostRoot Fiber)的核心函数,负责协调根组件的更新逻辑,包括状态处理、子节点 reconciliation、服务端渲染水合(Hydration)等。

javascript 复制代码
function updateHostRoot(
  current: null | Fiber,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  // 推入根组件的上下文,确保子组件能访问正确的根状态。
  // pushHostRootContext(workInProgress);

  if (current === null) {
    throw new Error('Should have a current fiber. This is a bug in React.');
  }

  // 获取新 props 和旧状态
  const nextProps = workInProgress.pendingProps;
  const prevState = workInProgress.memoizedState;
  const prevChildren = prevState.element;

  // 复制当前更新队列到新的 workInProgress Fiber,确保更新操作的不可变性。
  cloneUpdateQueue(current, workInProgress);
  // 计算新的根状态(nextState),处理状态更新(如 setState)和副作用。
  processUpdateQueue(workInProgress, nextProps, null, renderLanes);

  const nextState: RootState = workInProgress.memoizedState;
  const root: FiberRoot = workInProgress.stateNode;
  // pushRootTransition(workInProgress, root, renderLanes);

 
  // suspendIfUpdateReadFromEntangledAsyncAction();

  const nextChildren = nextState.element;
  
  if (supportsHydration && prevState.isDehydrated) {
    
  

  } else {
  
    // resetHydrationState();
    
    // 若新子节点与旧子节点相同,通过 bailout 跳过调和过程,直接复用现有 Fiber。
    if (nextChildren === prevChildren) {
      return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    }

    // 当子节点变化时,通过 reconcileChildren 生成新的子 Fiber 树,进入 diff 流程。
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  }
  return workInProgress.child;
}

工具函数之 cloneUpdateQueue

cloneUpdateQueue 是 React 内部用于克隆更新队列(Update Queue)的核心函数,主要作用是在调和(Reconciliation)阶段为 workInProgress Fiber 创建当前 Fiber(current)的更新队列副本。

javascript 复制代码
function cloneUpdateQueue<State>(
  current: Fiber,
  workInProgress: Fiber,
): void {
  // Clone the update queue from current. Unless it's already a clone.
  const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
  const currentQueue: UpdateQueue<State> = (current.updateQueue: any);

   // 仅当新旧队列引用相同时才执行克隆(避免重复克隆)
  if (queue === currentQueue) {
    const clone: UpdateQueue<State> = {
      baseState: currentQueue.baseState,
      firstBaseUpdate: currentQueue.firstBaseUpdate,
      lastBaseUpdate: currentQueue.lastBaseUpdate,
      shared: currentQueue.shared,
      callbacks: null, // 重置回调,避免旧回调影响新 Fiber
    };
    workInProgress.updateQueue = clone;
  }
}
javascript 复制代码
interface UpdateQueue<State> {
  baseState: State;                // 基础状态(未处理更新的状态)
  firstBaseUpdate: Update<State>;  // 第一个未处理的更新
  lastBaseUpdate: Update<State>;   // 最后一个未处理的更新
  shared: { [key: string]: mixed }; // 共享状态(如上下文)
  callbacks: Callback | null;      // 更新回调函数
}

工具函数之 processUpdateQueue

processUpdateQueue函数的核心作用是将组件的状态更新队列(queue)中的更新合并到当前状态(inst.state),确保状态按顺序更新,并处理函数式更新和对象合并逻辑。

javascript 复制代码
function processUpdateQueue(
  internalInstance: InternalInstance,
  inst: any,
  props: any,
  maskedLegacyContext: any,
): void {
  if (internalInstance.queue !== null && internalInstance.queue.length > 0) {
    // 处理更新队列
    
    const oldQueue = internalInstance.queue;
    // oldReplace:表示是否用新状态完全替换旧状态(而非合并)。
    const oldReplace = internalInstance.replace;
    // 重置队列为 null,避免重复处理
    internalInstance.queue = null;
    // 重置替换标志
    internalInstance.replace = false;

    // 仅一个更新项,直接替换状态。
    if (oldReplace && oldQueue.length === 1) {
      inst.state = oldQueue[0];
      
    } else {
      // 初始化 nextState
      let nextState = oldReplace ? oldQueue[0] : inst.state;
      // 是否需要创建新对象(避免直接修改旧状态)
      let dontMutate = true;

      // 遍历更新队列(从第一个有效更新开始)
      for (let i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
         // 获取当前更新项
        const partial = oldQueue[i];
        
        const partialState =
          typeof partial === 'function'
          // 处理函数式更新(如 setState((state) => ({ ...state, count: state.count + 1 })))
            ? partial.call(inst, nextState, props, maskedLegacyContext)
          // 非函数式更新直接使用值
            : partial;
        
        if (partialState != null) {
          if (dontMutate) {
            // 首次修改,创建新对象以保持不可变性
            dontMutate = false;
            nextState = assign({}, nextState, partialState);
          } else {
            // 后续修改直接合并(已创建新对象,可安全修改)
            assign(nextState, partialState);
          }
        }
      }
      
      // 最终状态赋值
      inst.state = nextState;
    }
    
  } else {
    // 清空空队列
    internalInstance.queue = null;
  }
}

<Tag>HostHoistable

React 渲染流程中处理 可提升资源节点(HostHoistable) 。当浏览器支持资源优化(supportsResourcestrue)时,会调用 updateHostHoistable 函数对这类节点进行特殊处理,以优化关键资源(如 CSS、JavaScript)的加载顺序和优先级,从而提升应用性能。

javascript 复制代码
case HostHoistable:
  if (supportsResources) {
    return updateHostHoistable(current, workInProgress, renderLanes);
  }

updateHostHoistable

updateHostHoistable 是 React 渲染流程中处理 可提升资源节点(如 CSS、JS 等) 的核心函数。

优化关键资源的加载执行流程:

  1. 标记 ref 相关副作用
  2. 初始化或更新资源对象(通过 getResource
  3. 管理资源状态(存储在 memoizedState 中)
  4. 决定是否创建 DOM 实例(非资源节点或非水合模式下)
javascript 复制代码
function updateHostHoistable(
  current: null | Fiber,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  // 标记出来ref相关副作用
  markRef(current, workInProgress);

  // 首次渲染
  if (current === null) {
    // 通过 getResource 获取资源对象(如 CSS、JS 资源)
    const resource = getResource(
      workInProgress.type,
      null,
      workInProgress.pendingProps,
      null,
    );
    
    if (resource) {
      // 将资源对象存储在 memoizedState 中
      workInProgress.memoizedState = resource;
    } else {
      if (!getIsHydrating()) {
        // This is not a Resource Hoistable and we aren't hydrating so we construct the instance.
        // 创建非资源类型的可提升节点(如普通 DOM 元素)
        workInProgress.stateNode = createHoistableInstance(
          workInProgress.type,
          workInProgress.pendingProps,
          getRootHostContainer(),
          workInProgress,
        );
      }
    }
  } else {
    // Get Resource may or may not return a resource. either way we stash the result
    // on memoized state.
    // 对比新旧 props,决定是否需要更新资源
    // 复用或创建新资源对象,更新到 memoizedState
    workInProgress.memoizedState = getResource(
      workInProgress.type,
      current.memoizedProps,
      workInProgress.pendingProps,
      current.memoizedState,
    );
  }

  return null;
}

工具函数之 markRef

markRef 函数主要作用是标记 workInProgress Fiber 是否需要执行 ref 相关的副作用(Effect)。

javascript 复制代码
function markRef(current: Fiber | null, workInProgress: Fiber) {
 
  const ref = workInProgress.ref;
  if (ref === null) {
    if (current !== null && current.ref !== null) {
      // Schedule a Ref effect
      workInProgress.flags |= Ref | RefStatic;
    }
  } else {
    if (typeof ref !== 'function' && typeof ref !== 'object') {
      throw new Error(
        'Expected ref to be a function, an object returned by React.createRef(), or undefined/null.',
      );
    }
    if (current === null || current.ref !== ref) {
      // Schedule a Ref effect
      workInProgress.flags |= Ref | RefStatic;
    }
  }
}

createHoistableInstance

创建可提升实例

javascript 复制代码
function createHoistableInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  internalInstanceHandle: Object,
): Instance {
  //n获取document对象
  const ownerDocument = getOwnerDocumentFromRootContainer(
    rootContainerInstance,
  );

  // 创建element
  const domElement: Instance = ownerDocument.createElement(type);

  // 用于建立 DOM 节点与 Fiber 节点双向映射
  precacheFiberNode(internalInstanceHandle, domElement);

  // 将 JSX 中的 props 信息存储到对应 DOM 节点上
  updateFiberProps(domElement, props);

  
  setInitialProperties(domElement, type, props);

  // 标记 DOM 节点为 可提升(Hoistable) 
  markNodeAsHoistable(domElement);
  return domElement;
}

工具函数之 precacheFiberNode

precacheFiberNode 是 React 内部用于建立 DOM 节点与 Fiber 节点双向映射的核心函数。

javascript 复制代码
function precacheFiberNode(
  hostInst: Fiber,
  node: Instance | TextInstance | SuspenseInstance | ReactScopeInstance,
): void {
  (node: any)[internalInstanceKey] = hostInst;
}

const internalInstanceKey = '__reactFiber$' + randomKey;

工具函数之 updateFiberProps

updateFiberProps 是 React 内部用于将 JSX 中的 props 信息存储到对应 DOM 节点上的工具函数。

javascript 复制代码
function updateFiberProps(
  node: Instance | TextInstance | SuspenseInstance,
  props: Props,
): void {
  (node: any)[internalPropsKey] = props;
}

const internalPropsKey = '__reactProps$' + randomKey;

工具函数之 markNodeAsHoistable

markNodeAsHoistable 是 React 内部用于标记 DOM 节点为 可提升(Hoistable) 的工具函数。

javascript 复制代码
function markNodeAsHoistable(node: Node) {
  (node: any)[internalHoistableMarker] = true;
}

const internalHoistableMarker = '__reactMarker$' + randomKey;

getResource

getResource 是 React 内部用于管理和复用关键资源(如 CSS、JavaScript 文件)的核心函数。它通过资源根节点(resourceRoot)追踪已加载的资源,实现资源共享、预加载和避免重复加载,从而优化应用性能。

javascript 复制代码
 function getResource(
  type: string,
  currentProps: any,
  pendingProps: any,
  currentResource: null | Resource,
): null | Resource {
   // 获取资源根节点
  const resourceRoot = getCurrentResourceRoot();
  if (!resourceRoot) {
    throw new Error(
      '"resourceRoot" was expected to exist. This is a bug in React.',
    );
  }
  switch (type) {
    case 'meta':
    case 'title': {
      return null;
    }
      // 处理资源类型style
    case 'style': {
      if (
        // precedence:样式优先级(如 'high'、'low'),用于控制加载顺序。
        typeof pendingProps.precedence === 'string' &&
        // href:外部样式表的 URL。
        typeof pendingProps.href === 'string'
      ) {
         // 资源键生成。getStyleKey:生成 URL 的唯一哈希值,确保相同 URL 的资源被复用。
        const key = getStyleKey(pendingProps.href);
        // 查找资源。hoistableStyles:Map 对象,键为资源 URL 哈希值,值为资源对象。
        const styles = getResourcesFromRoot(resourceRoot).hoistableStyles;
        
        let resource = styles.get(key);
        if (!resource) {
          // 资源对象结构
          
          resource = {
            type: 'style', // 资源类型
            instance: null, // DOM 实例(加载后填充)
            count: 0, // 引用计数(用于垃圾回收)
            state: null, // 资源状态(如加载中、已完成)
          };

          // 将新资源存入缓存
          styles.set(key, resource);
        }
        return resource;
      }

      // 无效资源处理
      return {
        type: 'void', // 特殊类型,表示无效资源
        instance: null,
        count: 0,
        state: null,
      };
      
    }

      // 处理资源类型link
    case 'link': {
      if (
        // rel="stylesheet":确认是样式表资源
        pendingProps.rel === 'stylesheet' &&
        // href 和 precedence 存在且类型正确
        typeof pendingProps.href === 'string' &&
        typeof pendingProps.precedence === 'string'
      ) {
        const qualifiedProps: StylesheetQualifyingProps = pendingProps;
        // 生成唯一key
        const key = getStyleKey(qualifiedProps.href);

        // 查找资源
        const styles = getResourcesFromRoot(resourceRoot).hoistableStyles;

        let resource = styles.get(key);
        
        if (!resource) {
          
          const ownerDocument = getDocumentFromRoot(resourceRoot);
          
          // 定义资源结构
          resource = ({
            type: 'stylesheet',
            instance: null, // 对应 DOM 元素
            count: 0, // 引用计数
            state: {
              loading: NotLoaded, // 加载状态
              preload: null, // 预加载 Promise
            },
          }: StylesheetResource);

          // 存入缓存
          styles.set(key, resource);

          // 通过 CSS 选择器查找已存在的 <link> 标签
          const instance = ownerDocument.querySelector(
            getStylesheetSelectorFromKey(key),
          );
          if (instance) {
            // 使用内部属性 _p 判断加载状态
            const loadingState: ?Promise<mixed> = (instance: any)._p;
            if (loadingState) {
              // This instance is inserted as part of a boundary reveal and is not yet
              // loaded
              // 正在加载中
            } else {
              // 实例已经加载完成
              // This instance is already loaded
              resource.instance = instance;
              resource.state.loading = Loaded | Inserted;
            }
          }

          // 预加载机制流程:
            // 生成预加载配置(如 as="style")
            // 创建 <link rel="preload"> 标签
            // 监听加载完成事件,更新资源状态
          if (!preloadPropsMap.has(key)) {
            const preloadProps = preloadPropsFromStylesheet(qualifiedProps);
            preloadPropsMap.set(key, preloadProps);
            if (!instance) {
              preloadStylesheet(
                ownerDocument,
                key,
                preloadProps,
                resource.state,
              );
            }
          }
        }
        
        // if (currentProps && currentResource === null) {
        //   let diff = '';
        // }
        return resource;
        
      } else {
        // if (currentProps && currentResource !== null) {
        //   let diff = '';
        // }
        return null;
      }
    }
    case 'script': {
      const async = pendingProps.async;
      const src = pendingProps.src;
      if (
        typeof src === 'string' &&
        async &&
        typeof async !== 'function' &&
        typeof async !== 'symbol'
      ) {
        const key = getScriptKey(src);
        const scripts = getResourcesFromRoot(resourceRoot).hoistableScripts;

        let resource = scripts.get(key);
        if (!resource) {
          
          resource = {
            type: 'script',
            instance: null,
            count: 0,
            state: null,
          };
          
          scripts.set(key, resource);
        }
        return resource;
      }
      return  {
        type: 'void',
        instance: null,
        count: 0,
        state: null,
      };
      
    }
    default: {
      throw new Error(
        `getResource encountered a type it did not expect: "${type}". this is a bug in React.`,
      );
    }
  }
}

1、处理style类型的资源

处理 外部样式表资源( **<style>**标签) 的逻辑分支。它通过 precedencehref 属性识别需要优化加载的样式资源,利用缓存机制避免重复加载,并返回标准化的资源对象。

2、处理link类型的资源

React 内部处理 <link rel="stylesheet"> 资源的核心逻辑,主要负责:

  1. 资源去重:通过 URL 缓存避免重复加载同一 CSS 文件
  2. 状态管理:跟踪样式表的加载状态(未加载、加载中、已完成)
  3. 预加载优化 :使用 <link rel="preload"> 加速关键样式的加载
  4. 服务端渲染集成:支持与 SSR 流程配合,提前收集关键 CSS

3、处理script类型的资源

工具函数之 preloadPropsFromStylesheet

preloadPropsFromStylesheet 是 React 内部用于将 <link rel="stylesheet"> 转换为 <link rel="preload"> 的工具函数。

javascript 复制代码
function preloadPropsFromStylesheet(
  props: StylesheetQualifyingProps,
): PreloadProps {
  return {
    rel: 'preload', // 将 rel 从 'stylesheet' 改为 'preload'
    as: 'style', // 指定资源类型为样式表
    href: props.href, // 保留原始 URL
    crossOrigin: props.crossOrigin, // 跨域配置
    integrity: props.integrity,  // 内容完整性校验
    media: props.media, // 媒体查询条件
    hrefLang: props.hrefLang,  // 语言设置
    referrerPolicy: props.referrerPolicy, // 引用策略
  };
}
html 复制代码
// 原始 JSX
<link rel="stylesheet" href="critical.css" precedence="high" />

// React 处理后
<link rel="preload" as="style" href="critical.css" />  // 预加载阶段
<link rel="stylesheet" href="critical.css" />         // 正式应用阶段

工具函数之 preloadStylesheet

preloadStylesheet 是 React 内部用于预加载 CSS 资源的核心函数,通过创建 <link rel="preload"> 标签实现样式表的并行加载,避免阻塞主线程渲染。

javascript 复制代码
function preloadStylesheet(
  ownerDocument: Document,
  key: string,
  preloadProps: PreloadProps,
  state: StylesheetState,
) {
  // 检查已有预加载标签
  const preloadEl = ownerDocument.querySelector(
    getPreloadStylesheetSelectorFromKey(key),
  );
  if (preloadEl) {
    // If we find a preload already it was SSR'd and we won't have an actual
    // loading state to track. For now we will just assume it is loaded
    // 服务端渲染已插入预加载标签,直接标记为已加载
    state.loading = Loaded;
  } else {
    // 创建预加载标签
    const instance = ownerDocument.createElement('link');
    state.preload = instance;
    
    // 监听加载事件
    instance.addEventListener('load', () => (state.loading |= Loaded));
    instance.addEventListener('error', () => (state.loading |= Errored));

    // 设置标签属性并插入文档
    setInitialProperties(instance, 'link', preloadProps);
    markNodeAsHoistable(instance); // 标记为可提升资源
    (ownerDocument.head: any).appendChild(instance); // 插入到文档头部
  }
}
javascript 复制代码
function getPreloadStylesheetSelectorFromKey(key: string) {
  return `link[rel="preload"][as="style"][${key}]`;
}

工具函数之 getCurrentResourceRoot

getCurrentResourceRoot() 是 React 内部用于获取当前渲染环境中 可提升资源根节点(HoistableRoot) 的工具函数。

javascript 复制代码
function getCurrentResourceRoot(): null | HoistableRoot {
  // 获取当前根容器
  const currentContainer = getCurrentRootHostContainer();
  // getHoistableRoot提取可提升资源根节点
  return currentContainer ? getHoistableRoot(currentContainer) : null;
}
javascript 复制代码
function getCurrentRootHostContainer(): null | Container {
  return rootInstanceStackCursor.current;
}
javascript 复制代码
function createCursor<T>(defaultValue: T): StackCursor<T> {
  return {
    current: defaultValue,
  };
}

工具函数之 getHoistableRoot

javascript 复制代码
function getHoistableRoot(container: Container): HoistableRoot {
  return typeof container.getRootNode === 'function'
    // Node 接口的 getRootNode() 方法返回上下文中的根节点
    ?  container.getRootNode()
    // Node.ownerDocument 只读属性会返回当前节点的顶层的 document 对象
    : container.ownerDocument;
}

工具函数之 getResourcesFromRoot

javascript 复制代码
function getResourcesFromRoot(root: HoistableRoot): RootResources {
  // 获取节点上的设置的资源
  let resources = (root: any)[internalRootNodeResourcesKey];
  if (!resources) {
    // 设置
    resources = (root: any)[internalRootNodeResourcesKey] = {
      hoistableStyles: new Map(),
      hoistableScripts: new Map(),
    };
  }
  return resources;
}

const internalRootNodeResourcesKey = '__reactResources$' + randomKey;

<Tag>HostSingleton

React 渲染流程中处理 单节点宿主组件。

javascript 复制代码
// Fall through
case HostSingleton:
  if (supportsSingletons) {
    return updateHostSingleton(current, workInProgress, renderLanes);
  }

updateHostSingleton

updateHostSingleton 是 React 渲染流程中处理 单节点宿主组件(如

****、 ****等 DOM 元素) 的核心函数。

javascript 复制代码
function updateHostSingleton(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  // 将当前 Fiber 节点的上下文(如命名空间、文档模式)压入栈中
  pushHostContext(workInProgress);

  if (current === null) {
    // 服务端渲染水合处理
    // claimHydratableSingleton(workInProgress);
  }

  const nextChildren = workInProgress.pendingProps.children;

  // 首次渲染
  if (current === null && !getIsHydrating()) {
    // 使用 reconcileChildFibers 创建新的子 Fiber 树
    workInProgress.child = reconcileChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderLanes,
    );
  } else {
    // 更新
    // 使用 reconcileChildren 对比新旧子节点,执行最小化 DOM 操作
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  }
  // 标记ref副作用
  markRef(current, workInProgress);
   // 返回子 Fiber 节点
  return workInProgress.child;
}

<Tag>HostComponent

React 渲染流程中处理 宿主组件(如 HTML 标签)。

javascript 复制代码
// Fall through
case HostComponent:
  return updateHostComponent(current, workInProgress, renderLanes);

工具函数之 updateHostComponent

updateHostComponent 是 React 渲染流程中处理 宿主组件(如 HTML 标签) 的核心函数。

javascript 复制代码
function updateHostComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  if (current === null) {
    // tryToClaimNextHydratableInstance(workInProgress);
  }

  // 设置当前渲染上下文
  pushHostContext(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);

  // 对于纯文本子节点(如 <div>Hello</div>),直接设置 textContent
  // 避免创建额外的文本节点,提升渲染效率
  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.flags |= ContentReset;
  }

  // 异步状态处理(并发模式)
  if (enableAsyncActions) {
    const memoizedState = workInProgress.memoizedState;
    if (memoizedState !== null) {

       // renderTransitionAwareHostComponentWithHooks 处理异步更新
      const newState = renderTransitionAwareHostComponentWithHooks(
        current,
        workInProgress,
        renderLanes,
      );
      
      // 根据渲染器类型(主渲染器或辅助渲染器)更新上下文值
      if (isPrimaryRenderer) {
        // _currentValue 存储主渲染器的上下文值
        HostTransitionContext._currentValue = newState;
      } else {
        // _currentValue2 存储辅助渲染器的上下文值
        HostTransitionContext._currentValue2 = newState;
      }

      // 懒传播
      if (enableLazyContextPropagation) {
        // In the lazy propagation implementation, we don't scan for matching
        // consumers until something bails out.
      } else {
        if (didReceiveUpdate) {
          if (current !== null) {
            const oldStateHook: Hook = current.memoizedState;
            const oldState: TransitionStatus = oldStateHook.memoizedState;
          
            if (oldState !== newState) {
              // 更新上下文并传播变化
              propagateContextChange(
                workInProgress,
                HostTransitionContext,
                renderLanes,
              );
            }
          }
        }
      }
    }
  }

  markRef(current, workInProgress);
  // 协调子节点
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

renderTransitionAwareHostComponentWithHooks

处理hook钩子函数。

javascript 复制代码
function renderTransitionAwareHostComponentWithHooks(
  current: Fiber | null,
  workInProgress: Fiber,
  lanes: Lanes,
): TransitionStatus {
  if (!enableAsyncActions) {
    throw new Error('Not implemented.');
  }
  return renderWithHooks(
    current,
    workInProgress,
    TransitionAwareHostComponent,
    null,
    null,
    lanes,
  );
}

工具函数之 propagateContextChange

javascript 复制代码
function propagateContextChange<T>(
  workInProgress: Fiber,
  context: ReactContext<T>,
  renderLanes: Lanes,
): void {
  if (enableLazyContextPropagation) {
   
    const forcePropagateEntireTree = true;
    propagateContextChanges(
      workInProgress,
      [context],
      renderLanes,
      forcePropagateEntireTree,
    );
  } else {
    propagateContextChange_eager(workInProgress, context, renderLanes);
  }
}

工具函数之 propagateContextChange_eager

propagateContextChange_eager 是 React 中用于 主动传播上下文变化 的核心函数(适用于非懒传播模式)。当上下文(Context)的值发生改变时,该函数会遍历相关 Fiber 树,找到依赖该上下文的组件并为其标记更新,确保组件能响应上下文变化并重新渲染。

javascript 复制代码
function propagateContextChange_eager<T>(
  workInProgress: Fiber,
  context: ReactContext<T>,
  renderLanes: Lanes,
): void {
  // Only used by eager implementation
  if (enableLazyContextPropagation) {
    return;
  }

  // Fiber 树遍历初始化
  let fiber = workInProgress.child;
  if (fiber !== null) {
    // Set the return pointer of the child to the work-in-progress fiber.
    // 设置子节点的 return 指针指向父节点
    fiber.return = workInProgress;
  }
  // 深度优先遍历循环
  while (fiber !== null) {
    let nextFiber;

    // Visit this fiber.
    // 获取当前节点依赖的上下文列表
    const list = fiber.dependencies;
    if (list !== null) {
      nextFiber = fiber.child;

      let dependency = list.firstContext;
      while (dependency !== null) {
        // Check if the context matches.
         // 匹配到目标上下文,触发更新逻辑
        if (dependency.context === context) {
          
          // Match! Schedule an update on this fiber.
             // 为类组件生成强制更新
          if (fiber.tag === ClassComponent) {
            // Schedule a force update on the work-in-progress.
            const lane = pickArbitraryLane(renderLanes);
            
            const update = createUpdate(lane);
            update.tag = ForceUpdate;
 
            // Inlined `enqueueUpdate` to remove interleaved update check
             // 入队更新(直接操作队列,避免中间检查)
            const updateQueue = fiber.updateQueue;
            if (updateQueue === null) {
              // Only occurs if the fiber has been unmounted.
            } else {
              const sharedQueue: SharedQueue<any> = (updateQueue: any).shared;
              const pending = sharedQueue.pending;

              // update是是一个循环链表结构
              if (pending === null) {
                // This is the first update. Create a circular list.
                update.next = update;
              } else {
                update.next = pending.next;
                pending.next = update;
              }
              sharedQueue.pending = update;
            }
          }

          // 标记当前节点及其 alternate 节点的更新优先级
          fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
          const alternate = fiber.alternate;
          if (alternate !== null) {
            // 合并车道
            alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
          }
           // 向上传播任务到父路径
          scheduleContextWorkOnParentPath(
            fiber.return,
            renderLanes,
            workInProgress,
          );

          // Mark the updated lanes on the list, too.
          list.lanes = mergeLanes(list.lanes, renderLanes);

          // Since we already found a match, we can stop traversing the
          // dependency list.
          // 找到一个匹配后,无需继续遍历依赖列表
          break;
        }
        dependency = dependency.next;
      }
    } else if (fiber.tag === ContextProvider) {
      // Don't scan deeper if this is a matching provider
        // 若当前节点是匹配的 Provider,停止向下遍历(避免重复处理)
      nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
      
    } else if (fiber.tag === DehydratedFragment) {
       // 处理 Suspense 边界的脱水片段,标记其父 Suspense 节点
      const parentSuspense = fiber.return;

      if (parentSuspense === null) {
        throw new Error(
          'We just came from a parent so we must have had a parent. This is a bug in React.',
        );
      }

      parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
      const alternate = parentSuspense.alternate;
      if (alternate !== null) {
        alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
      }

      scheduleContextWorkOnParentPath(
        parentSuspense,
        renderLanes,
        workInProgress,
      );
      // 跳过子树,直接处理兄弟节点
      nextFiber = fiber.sibling;
    } else {
      // Traverse down.
      nextFiber = fiber.child;
    }

    if (nextFiber !== null) {
      // Set the return pointer of the child to the work-in-progress fiber.
      nextFiber.return = fiber;
    } else {
      // No child. Traverse to next sibling.
      nextFiber = fiber;
      while (nextFiber !== null) {
        if (nextFiber === workInProgress) {
          // We're back to the root of this subtree. Exit.
          nextFiber = null;
          break;
        }
        const sibling = nextFiber.sibling;
        if (sibling !== null) {
          // Set the return pointer of the sibling to the work-in-progress fiber.
          sibling.return = nextFiber.return;
          nextFiber = sibling;
          break;
        }
        // No more siblings. Traverse up.
        nextFiber = nextFiber.return;
      }
    }
    fiber = nextFiber;
  }
}

工具函数之 scheduleContextWorkOnParentPath

scheduleContextWorkOnParentPath 是 React 中用于 向上传播上下文更新任务 的核心函数

javascript 复制代码
function scheduleContextWorkOnParentPath(
  parent: Fiber | null,
  renderLanes: Lanes,
  propagationRoot: Fiber,
) {
  // Update the child lanes of all the ancestors, including the alternates.
  let node = parent;
  // 从当前父节点出发遍历
  while (node !== null) {
    // 获取备用 Fiber 节点(双缓冲机制)
    const alternate = node.alternate;

    // 检查当前节点的 childLanes 是否包含新的 renderLanes
    if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
      // 合并车道
      node.childLanes = mergeLanes(node.childLanes, renderLanes);
      
      if (alternate !== null) {
        // 同步更新备用节点
        alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
      }
    } else if (
      alternate !== null &&
      !isSubsetOfLanes(alternate.childLanes, renderLanes)
    ) {
      alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
    }
    //  到达传播根节点,停止遍历
    if (node === propagationRoot) {
      break;
    }
    // 向上处理父节点
    node = node.return;
  }
}

<Tag>HostText

javascript 复制代码
case HostText:
  return updateHostText(current, workInProgress);

updateHostText

javascript 复制代码
function updateHostText(current: null | Fiber, workInProgress: Fiber) {
  // 说明首次渲染或没有可对比的旧fiber
  if (current === null) {
    //tryToClaimNextHydratableTextInstance(workInProgress);
  }

  return null;
}

<Tag>HostPortal

javascript 复制代码
case HostPortal:
  return updatePortalComponent(current, workInProgress, renderLanes);

updatePortalComponent

javascript 复制代码
function updatePortalComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
  
  const nextChildren = workInProgress.pendingProps;
  if (current === null) {
    
    workInProgress.child = reconcileChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderLanes,
    );
  } else {
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  }
  return workInProgress.child;
}

渲染阶段二completeUnitOfWork

completeUnitOfWork 函数在 React 的协调过程中负责完成单个 Fiber 节点的工作单元。它会不断尝试完成当前 Fiber 节点的工作,处理可能出现的未完成情况,根据处理结果决定是继续处理兄弟节点还是返回父节点,直到完成整个 Fiber 树的工作,并在到达根节点时更新根节点的退出状态。

执行过程:

  1. 初始化当前要处理的 filbert 节点,定义为completedWork。
  2. do...while 循环,直到completedWork 为 null。
    1. 如果 fiber 是否存在未完成的工作,调用 unwindUnitOfWork 函数处理未完成的工作,然后跳出 do...while 循环。
    2. 没有未完成的工作,则调用 completeWork 完成当前节点的工作。
javascript 复制代码
function completeUnitOfWork(unitOfWork: Fiber): void {

  // completedWork 初始化为传入的 unitOfWork
  let completedWork: Fiber = unitOfWork;

  // 使用 do-while 循环来处理 Fiber 节点的工作
  do {
    // 检查 completedWork 的 flags 属性中是否包含 Incomplete 标志。如果包含,说明当前 Fiber 节点的工作未完成。
    if ((completedWork.flags & Incomplete) !== NoFlags) {
      const skipSiblings = workInProgressRootDidSkipSuspendedSiblings;

      // 用 unwindUnitOfWork 函数处理未完成的工作
      unwindUnitOfWork(completedWork, skipSiblings);
      return;
    }

   // 获取当前 Fiber 节点的替代节点,用于存储旧的状态。
    const current = completedWork.alternate;
    
    // 获取当前 Fiber 节点的父节点,用于在完成当前节点工作后返回父节点继续处理。
    const returnFiber = completedWork.return;

    // 用于存储 completeWork 函数返回的下一个要处理的 Fiber 节点。
    let next;
    // 调用 startProfilerTimer 函数开始记录性能分析时间。
    // startProfilerTimer(completedWork);

    // 调用 completeWork 函数完成当前 Fiber 节点的工作,传入当前 Fiber 节点的替代节点 current、当前 Fiber 节点 completedWork 以及相关的渲染车道 entangledRenderLanes。
    next = completeWork(current, completedWork, entangledRenderLanes);


    // 如果 next 不为 null,说明完成当前 Fiber 节点的工作产生了新的工作。
    if (next !== null) {
      // Completing this fiber spawned new work. Work on that next.
      // 将 next 赋值给 workInProgress,表示下一个要处理的 Fiber 节点成为当前的工作进行中的节点,函数直接返回。
      workInProgress = next;
      return;
    }

    // 获取当前 Fiber 节点的兄弟节点 siblingFiber。
    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      // If there is more work to do in this returnFiber, do that next.
      // 如果兄弟节点不为 null,将兄弟节点赋值给 workInProgress,表示接下来要处理兄弟节点的工作,函数直接返回。
      workInProgress = siblingFiber;
      return;
    }

    // 如果没有新产生的工作,也没有兄弟节点,将 completedWork 更新为其父节点 returnFiber,同时更新 workInProgress 为父节点,继续处理父节点的工作。
   
    completedWork = returnFiber;
    // Update the next thing we're working on in case something throws.
    workInProgress = completedWork;
    
    // 只要 completedWork 不为 null,就会继续循环处理。
  } while (completedWork !== null);

  // We've reached the root.
  // 如果根节点的退出状态为 RootInProgress(正在进行中),将其更新为 RootCompleted(已完成)。
  if (workInProgressRootExitStatus === RootInProgress) {
    workInProgressRootExitStatus = RootCompleted;
  }
}

completeWork

completeWork 函数的主要职责是根据当前 Fiber 节点的类型(通过 workInProgress.tag 标识),对该节点进行相应的处理,比如更新 DOM、设置属性等。(负责单个fiber节点的处理)

javascript 复制代码
function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
  
  const newProps = workInProgress.pendingProps;
  popTreeContext(workInProgress);
  
  switch (workInProgress.tag) {
      // 省略的代码

    case Throw: {
      if (!disableLegacyMode) {
        // Only Legacy Mode completes an errored node.
        return null;
      }
    }
  }

  throw new Error(
    `Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
      'React. Please file an issue.',
  );
}

<Tag>FunctionComponent/LazyComponent/ForwardRef/...

javascript 复制代码
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
  bubbleProperties(workInProgress);
  return null;

bubbleProperties

bubbleProperties 函数的主要功能是从一个已完成工作的 Fiber 节点(completedWork)的子节点中收集信息,将这些信息冒泡到该节点上。这些信息主要包括子节点的渲染优先级车道(Lanes)和副作用标志(Flags)。

javascript 复制代码
function bubbleProperties(completedWork: Fiber) {

  // 如果 completedWork 存在替代节点(alternate),并且替代节点的子节点与当前节点的子节点相同,则认为可以跳过更新。
  const didBailout =
    completedWork.alternate !== null &&
    completedWork.alternate.child === completedWork.child;

  // 用于存储合并后的子节点的渲染优先级车道,初始化为 NoLanes(表示没有车道)。
  let newChildLanes: Lanes = NoLanes;
  // 用于存储合并后的子节点的副作用标志,初始化为 NoFlags(表示没有标志)。
  let subtreeFlags = NoFlags;

   // 未跳过更新的情况
  if (!didBailout) {
    let child = completedWork.child;
    // 使用 while 循环遍历 completedWork 的所有子节点:
    while (child !== null) {
      // 合并子节点的 lanes 和 childLanes 到newChildLanes
      newChildLanes = mergeLanes(
        newChildLanes,
        mergeLanes(child.lanes, child.childLanes),
      );

      // 将子节点的 subtreeFlags 和 flags 合并到 subtreeFlags 中
      subtreeFlags |= child.subtreeFlags;
      subtreeFlags |= child.flags;
      
      child.return = completedWork;

      // 继续下一个兄弟节点
      child = child.sibling;
    }

    // 更新
    completedWork.subtreeFlags |= subtreeFlags;
    
  } else {
    
    let child = completedWork.child;
    while (child !== null) {
      // 合并子节点的 lanes 和 childLanes 到 newChildLanes 中。
      newChildLanes = mergeLanes(
        newChildLanes,
        mergeLanes(child.lanes, child.childLanes),
      );

      // ignore them.
      // 使用按位与运算符 & 过滤掉非静态的标志(通过 StaticMask)。
      subtreeFlags |= child.subtreeFlags & StaticMask;
      subtreeFlags |= child.flags & StaticMask;
      child.return = completedWork;
      child = child.sibling;
    }

    completedWork.subtreeFlags |= subtreeFlags;
  }

  completedWork.childLanes = newChildLanes;
  return didBailout;
}

<Tag>HostRoot

React 处理 HostRoot 类型节点(即应用根节点)的核心逻辑,主要负责完成根节点渲染后的环境清理、状态同步和副作用标记。

javascript 复制代码
case HostRoot: {
  // 1、获取 FiberRoot 实例,React 应用的根对象,包含应用状态、优先级队列等核心信息。
  const fiberRoot = (workInProgress.stateNode: FiberRoot);

  //2、缓存处理
  if (enableCache) {
    let previousCache: Cache | null = null;
    if (current !== null) {
      previousCache = current.memoizedState.cache;
    }
    const cache: Cache = workInProgress.memoizedState.cache;
    // 比较新旧缓存对象,若不同则标记 Passive 副作用(如 useEffect)
    if (cache !== previousCache) {
      // Run passive effects to retain/release the cache.
      workInProgress.flags |= Passive;
    }
    // 清理缓存上下文栈
    popCacheProvider(workInProgress, cache);
  }

  // 3、环境状态恢复
  // 弹出过渡状态:结束当前渲染的过渡(如 startTransition)。
  popRootTransition(workInProgress, fiberRoot, renderLanes);
  // 弹出宿主容器:恢复 DOM 根节点上下文(见 popHostContainer 函数)
  popHostContainer(workInProgress);
  // 弹出遗留上下文:清理旧版 context API 的状态。
  popTopLevelLegacyContextObject(workInProgress);

  // 4、上下文状态同步
  // 在渲染过程中收集的新上下文值,在此处应用到根节点。
  if (fiberRoot.pendingContext) {
    fiberRoot.context = fiberRoot.pendingContext;
    fiberRoot.pendingContext = null;
  }
  //5、处理水合
  // 处理首次渲染或无子节点的情况
  if (current === null || current.child === null) {
    // If we hydrated, pop so that we can delete any remaining children
    // that weren't hydrated.
    // popHydrationState:清理水合过程中的临时状态,并返回是否成功水合。
    const wasHydrated = popHydrationState(workInProgress);
    // 水合成功
    if (wasHydrated) {
      // emitPendingHydrationWarnings();
      // 标记更新,可能用于处理残留的 DOM 差异。
      // markUpdate(workInProgress);
    } else {
      // 未水合或水合失败的处理
      
      if (current !== null) {
        const prevState: RootState = current.memoizedState;
        if (
          // Check if this is a client root
           // 非脱水状态(纯客户端渲染)
          !prevState.isDehydrated ||
          // Check if we reverted to client rendering (e.g. due to an error)
          // 强制客户端渲染标志(如水合错误)
          (workInProgress.flags & ForceClientRender) !== NoFlags
        ) {
       // Snapshot 标志:触发 getSnapshotBeforeUpdate 生命周期(若适用)。
          workInProgress.flags |= Snapshot;
          // upgradeHydrationErrorsToRecoverable:将水合错误转换为可恢复错误,避免整页崩溃。
          upgradeHydrationErrorsToRecoverable();
        }
      }
    }
  }
  
  // 6、更新宿主容器与冒泡属性
  // 更新宿主容器:生成 DOM 更新队列(如属性变化、子节点增删)。
  updateHostContainer(current, workInProgress);
  // 冒泡属性:向上传递子树的状态(如是否有副作用、优先级信息)。
  bubbleProperties(workInProgress);
  return null;
}

updateHostContainer

updateHostContainer 是 React 中处理宿主容器(如 DOM 根节点或 Portal 容器)更新的核心函数。当启用持久化渲染(supportsPersistence)且需要克隆容器时,该函数会创建新的子节点集合,标记更新,并最终触发容器的子节点替换。

javascript 复制代码
function updateHostContainer(current: null | Fiber, workInProgress: Fiber) {
  // supportsPersistence:是否启用持久化渲染(如 React 18+ 的并发模式)。
  if (supportsPersistence) {

    // oesRequireClone:判断是否需要克隆容器(如子节点结构变化、属性更新等)。
    if (doesRequireClone(current, workInProgress)) {

     // 创建新子节点集合
      const portalOrRoot: {
        containerInfo: Container,
        pendingChildren: ChildSet,
        
      } = workInProgress.stateNode;
      const container = portalOrRoot.containerInfo;
      // 创建用于存储子节点的临时集合。
      const newChildSet = createContainerChildSet();
      // If children might have changed, we have to add them all to the set.
      // appendAllChildrenToContainer将 workInProgress 的所有子节点添加到新集合。
      appendAllChildrenToContainer(
        newChildSet,
        workInProgress,
        /* needsVisibilityToggle */ false,
        /* isHidden */ false,
      );
      portalOrRoot.pendingChildren = newChildSet;
      // Schedule an update on the container to swap out the container.
      // 标记容器需要更新,触发后续的 DOM 操作。
      markUpdate(workInProgress);
      // 准备容器的子节点更新,可能包括:
        // 计算新旧子节点的差异。
        // 准备插入 / 删除 / 移动等 DOM 操作队列。
      finalizeContainerChildren(container, newChildSet);
    }
  }
}

工具函数之 doesRequireClone

doesRequireClone 函数用于判断是否需要克隆宿主容器(如 DOM 节点)以应用更新。

javascript 复制代码
function doesRequireClone(current: null | Fiber, completedWork: Fiber) {
  const didBailout = current !== null && current.child === completedWork.child;
  // 若组件因 bailout 机制跳过渲染(如 React.memo 优化),直接返回 false(无需克隆)。
  if (didBailout) {
    return false;
  }

  // ChildDeletion 标志:表示子树中存在节点删除操作,需克隆容器以安全移除节点。
  if ((completedWork.flags & ChildDeletion) !== NoFlags) {
    return true;
  }

  // TODO: If we move the `doesRequireClone` call after `bubbleProperties`
  // then we only have to check the `completedWork.subtreeFlags`.
  let child = completedWork.child;
  while (child !== null) {
    // Cloned:节点需要被克隆。
    // Visibility:节点可见性变化(如通过 Suspense 控制)。
    // Placement:节点插入或移动。
    // ChildDeletion:子节点删除。
    // 持久化模式(enablePersistedModeClonedFlag 启用
    const checkedFlags = enablePersistedModeClonedFlag
      ? Cloned | Visibility | Placement | ChildDeletion
      // 传统模式:检查 MutationMask(包含 Placement、Update、Deletion 等基本变更)。
      : MutationMask;

    // child.flags:当前节点的变更标志。
    // child.subtreeFlags:子树中所有节点的累积变更标志。
    if (
      (child.flags & checkedFlags) !== NoFlags ||
      (child.subtreeFlags & checkedFlags) !== NoFlags
    ) {
      return true;
    }
    child = child.sibling;
  }
  return false;
}

工具函数之 createContainerChildSet

createContainerChildSet 是 React 中用于创建子节点集合的工厂函数,主要服务于宿主容器(如 DOM 根节点或 Portal)的更新过程。

javascript 复制代码
createContainerChildSet(): Array<Instance | TextInstance> {
  return [];
},

工具函数之 appendAllChildrenToContainer

appendAllChildrenToContainer 是 React 渲染流程中的核心函数,用于将 Fiber 树中的子节点递归转换为实际渲染节点(如 DOM 元素)并添加到容器中。

javascript 复制代码
function appendAllChildrenToContainer(
  containerChildSet: ChildSet, // 目标容器子节点集合
  workInProgress: Fiber, // 当前处理的 Fiber 节点
  needsVisibilityToggle: boolean, // 是否需要控制可见性
  isHidden: boolean,  // 当前是否隐藏
) {
  if (supportsPersistence) {
    // We only have the top Fiber that was created but we need recurse down its
    // children to find all the terminal nodes.
    let node = workInProgress.child;
    while (node !== null) {
      // 节点类型处理
      // 1、HostComponent:DOM 元素(如 <div>)。
      if (node.tag === HostComponent) {
        let instance = node.stateNode;
        if (needsVisibilityToggle && isHidden) {
          // This child is inside a timed out tree. Hide it.
          const props = node.memoizedProps;
          const type = node.type;
          instance = cloneHiddenInstance(instance, type, props);
        }
          
        // 将实例添加到子节点集合中
        appendChildToContainerChildSet(containerChildSet, instance);

        // 2、处理文本
      } else if (node.tag === HostText) {
        let instance = node.stateNode;
        if (needsVisibilityToggle && isHidden) {
          // This child is inside a timed out tree. Hide it.
          const text = node.memoizedProps;
          instance = cloneHiddenTextInstance(instance, text);
        }
        appendChildToContainerChildSet(containerChildSet, instance);

        // 3、处理HostPortal
      } else if (node.tag === HostPortal) {
        // Portal 节点不递归处理子节点(由 Portal 单独管理)

        // 4、处理不可见组件
      } else if (
        node.tag === OffscreenComponent &&
        node.memoizedState !== null
      ) {
        const child = node.child;
        if (child !== null) {
          child.return = node;
        }
        // If Offscreen is not in manual mode, detached tree is hidden from user space.
        const _needsVisibilityToggle = !isOffscreenManual(node);
        appendAllChildrenToContainer(
          containerChildSet,
          node,
          /* needsVisibilityToggle */ _needsVisibilityToggle,
          /* isHidden */ true,
        );

        // 递归到子节点
      } else if (node.child !== null) {
        
        node.child.return = node;
        node = node.child;
        continue;
      }
      
      node = (node: Fiber);
      if (node === workInProgress) {
        return;
      }

      // 回溯到父节点
      while (node.sibling === null) {
     
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        node = node.return;
      }
  
      node.sibling.return = node.return;
      node = node.sibling;
    }
  }
}

工具函数之 appendChildToContainerChildSet

appendChildToContainerChildSet 是 React 内部用于将子节点添加到容器子节点集合的辅助函数。

javascript 复制代码
appendChildToContainerChildSet(
   childSet: Array<Instance | TextInstance>,
   child: Instance | TextInstance,
 ): void {
    childSet.push(child);
},

工具函数之 appendAllChildrenToContainer

appendAllChildrenToContainer 是 React 渲染流程中的核心函数,用于将 Fiber 树中的子节点递归转换为实际渲染节点(如 DOM 元素)并添加到容器中。该函数处理多种节点类型(组件、文本、Portal 等),支持可见性控制,并在持久化渲染模式下优化节点添加逻辑。

javascript 复制代码
function appendAllChildrenToContainer(
  containerChildSet: ChildSet, // 目标容器子节点集合
  workInProgress: Fiber, // 当前处理的 Fiber 节点
  needsVisibilityToggle: boolean, // 是否需要控制可见性
  isHidden: boolean,  // 当前是否隐藏
) {
  if (supportsPersistence) {
    // We only have the top Fiber that was created but we need recurse down its
    // children to find all the terminal nodes.
    let node = workInProgress.child;
    while (node !== null) {
      // 节点类型处理
      // 1、HostComponent:DOM 元素(如 <div>)。
      if (node.tag === HostComponent) {
        let instance = node.stateNode;
        if (needsVisibilityToggle && isHidden) {
          // This child is inside a timed out tree. Hide it.
          const props = node.memoizedProps;
          const type = node.type;
          instance = cloneHiddenInstance(instance, type, props);
        }
        appendChildToContainerChildSet(containerChildSet, instance);

        // 2、处理文本
      } else if (node.tag === HostText) {
        let instance = node.stateNode;
        if (needsVisibilityToggle && isHidden) {
          // This child is inside a timed out tree. Hide it.
          const text = node.memoizedProps;
          instance = cloneHiddenTextInstance(instance, text);
        }
        appendChildToContainerChildSet(containerChildSet, instance);

        // 3、处理HostPortal
      } else if (node.tag === HostPortal) {
        // Portal 节点不递归处理子节点(由 Portal 单独管理)

        // 4、处理不可见组件
      } else if (
        node.tag === OffscreenComponent &&
        node.memoizedState !== null
      ) {
        const child = node.child;
        if (child !== null) {
          child.return = node;
        }
        // If Offscreen is not in manual mode, detached tree is hidden from user space.
        const _needsVisibilityToggle = !isOffscreenManual(node);
        appendAllChildrenToContainer(
          containerChildSet,
          node,
          /* needsVisibilityToggle */ _needsVisibilityToggle,
          /* isHidden */ true,
        );

        // 递归到子节点
      } else if (node.child !== null) {
        
        node.child.return = node;
        node = node.child;
        continue;
      }
      
      node = (node: Fiber);
      if (node === workInProgress) {
        return;
      }

      // 回溯或处理兄弟节点
      while (node.sibling === null) {
     
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        node = node.return;
      }
  
      node.sibling.return = node.return;
      node = node.sibling;
    }
  }
}

工具函数之 finalizeContainerChildren

finalizeContainerChildren 函数用于将新生成的子节点集合(newChildren)关联到容器(container)。

javascript 复制代码
finalizeContainerChildren(
  container: Container,
  newChildren: Array<Instance | TextInstance>,
): void {
  container.pendingChildren = newChildren;

  if (
    newChildren.length === 1 &&
    newChildren[0].text === 'Error when completing root'
  ) {
    // Trigger an error for testing purposes
    throw Error('Error when completing root');
  }
},

<Tag>HostHoistable

处理 React 中 HostHoistable 类型的 Fiber 节点,主要用于优化和管理可提升的宿主环境组件(如 DOM 元素)。这类组件可能包含异步资源(如图片、脚本),需要特殊的预加载和挂起逻辑。代码根据组件是否包含资源(Resource)以及是否处于更新阶段,选择不同的处理路径,确保资源预加载和组件渲染的高效性与正确性。

执行过程(启用资源管理):

一、初始化过程

  1. 标记更新。
  2. 处理资源类型。
  3. 处理普通实例类型。

二、更新过程

  1. 资源存在
    1. 资源存在变化。标记更新。
    2. 资源未发生变化。清除挂起标记。
  2. 普通实例
    1. 支持突变模式。当props发生变化化,标记更新。
    2. 不支持突变模式,调用updateHostComponent函数。
TypeScript 复制代码
case HostHoistable: {
  if (supportsResources) {
 
    const type = workInProgress.type;
    const nextResource: Resource | null = workInProgress.memoizedState;

    // 初始化
    if (current === null) {
      
      // 标记更新
      markUpdate(workInProgress);
      
      if (nextResource !== null) {
      // 处理资源类型
        
        bubbleProperties(workInProgress);
        preloadResourceAndSuspendIfNeeded(
          workInProgress,
          nextResource,
          type,
          newProps,
          renderLanes,
        );
        return null;
        
      } else {
        // 处理普通实例
        bubbleProperties(workInProgress);
        preloadInstanceAndSuspendIfNeeded(
          workInProgress,
          type,
          newProps,
          renderLanes,
        );
        return null;
      }
    } else {
      // This is an update.
      if (nextResource) {
        // This is a Resource
        if (nextResource !== current.memoizedState) {
          //  资源变化,触发更新和预加载
          // we have a new Resource. we need to update
          markUpdate(workInProgress);
          // This must come at the very end of the complete phase.
          bubbleProperties(workInProgress);
        
          preloadResourceAndSuspendIfNeeded(
            workInProgress,
            nextResource,
            type,
            newProps,
            renderLanes,
          );
          return null;
        } else {
          // This must come at the very end of the complete phase.
          bubbleProperties(workInProgress);
           // 资源未变化,清除挂起标志
          workInProgress.flags &= ~MaySuspendCommit;
          return null;
        }
      } else {
        // 更新阶段且为普通实例
        // This is an Instance
        // We may have props to update on the Hoistable instance.
        if (supportsMutation) {
          const oldProps = current.memoizedProps;
          if (oldProps !== newProps) {
            markUpdate(workInProgress);
          }
        } else {
          // We use the updateHostComponent path becuase it produces
          // the update queue we need for Hoistables.
          updateHostComponent(
            current,
            workInProgress,
            type,
            newProps,
            renderLanes,
          );
        }
        // This must come at the very end of the complete phase.
        bubbleProperties(workInProgress);
        preloadInstanceAndSuspendIfNeeded(
          workInProgress,
          type,
          newProps,
          renderLanes,
        );
        return null;
      }
    }
  }
  // Fall through
}

工具函数之 preloadResourceAndSuspendIfNeeded

preloadResourceAndSuspendIfNeeded 是 React 内部用于处理异步资源预加载的核心函数,主要针对 HostHoistable 类型的 Fiber 节点(如媒体元素、脚本等)。其核心逻辑是:尝试预加载资源,如果资源未就绪则挂起当前渲染,避免阻塞主线程。该函数实现了 React 的并发渲染特性,确保在资源加载期间页面保持响应,并根据配置决定是显示旧内容还是立即挂起。

javascript 复制代码
function preloadResourceAndSuspendIfNeeded(
  workInProgress: Fiber,
  resource: Resource,
  type: Type,
  props: Props,
  renderLanes: Lanes,
) {
  // This is a fork of preloadInstanceAndSuspendIfNeeded, but for resources.
  // mayResourceSuspendCommit 判断资源是否可能导致挂起。
  if (!mayResourceSuspendCommit(resource)) {
    // 若不可能挂起(如资源已缓存),清除挂起标志并直接返回。
    workInProgress.flags &= ~MaySuspendCommit;
    return;
  }

  // 标记可能挂起状态
  workInProgress.flags |= MaySuspendCommit;

  const isReady = preloadResource(resource);
  
  // 资源未就绪
  if (!isReady) {
    if (shouldRemainOnPreviousScreen()) {
      // 标记 ShouldSuspendCommit 标志,等待资源就绪后再提交更新。
      workInProgress.flags |= ShouldSuspendCommit;
    } else {
      // 资源已就绪
      // 立即调用 suspendCommit() 挂起当前渲染,触发 Suspense 边界的 fallback。
      suspendCommit();
    }
  }
}

<Tag>HostSingleton

处理 React 中 HostSingleton 类型的 Fiber 节点,用于表示必须在整个应用中唯一存在的宿主环境组件。

TypeScript 复制代码
case HostSingleton: {
  // 支持单例
  if (supportsSingletons) {
    // 弹出当前 Fiber 的宿主环境上下文。
    popHostContext(workInProgress);
    // 获取根应用节点
    const rootContainerInstance = getRootHostContainer();
    const type = workInProgress.type;
    // 更新
    if (current !== null && workInProgress.stateNode != null) {
      // 突变模式
      if (supportsMutation) {
        const oldProps = current.memoizedProps;
        if (oldProps !== newProps) {
          // 标记更新
          markUpdate(workInProgress);
        }
      } else {
        
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          renderLanes,
        );
      }
    } else {
      // 初始化
      if (!newProps) {
        if (workInProgress.stateNode === null) {
          throw new Error(
            'We must have new props for new mounts. This error is likely ' +
              'caused by a bug in React. Please file an issue.',
          );
        }

        // This can happen when we abort work.
        bubbleProperties(workInProgress);
        return null;
      }

      // 获取上下文
      const currentHostContext = getHostContext();
      const wasHydrated = popHydrationState(workInProgress);
      let instance: Instance;
      // 服务端渲染
      if (wasHydrated) {
        // prepareToHydrateHostInstance(workInProgress, currentHostContext);
        // // 复用现有 DOM 节点,避免重新创建。
        // instance = workInProgress.stateNode;
      } else {
        // 客户端渲染
        // 通过 resolveSingletonInstance 创建单例实例,并标记更新。
        instance = resolveSingletonInstance(
          type,
          newProps,
          rootContainerInstance,
          currentHostContext,
          true,
        );
        workInProgress.stateNode = instance;
        markUpdate(workInProgress);
      }
    }
    // 属性冒泡,将子节点的状态(如副作用标志)向上传播到父节点。
    bubbleProperties(workInProgress);
    // 结束当前 Fiber 的处理
    return null;
  }
  // Fall through
}

工具函数之 resolveSingletonInstance

resolveSingletonInstance 是 React 内部用于获取或创建单例 DOM 元素的核心函数,专门处理那些在文档中必须唯一存在且不能被 React 直接创建的特殊元素(如 <html><head><body>)。

javascript 复制代码
function resolveSingletonInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
  validateDOMNestingDev: boolean,
): Instance {
  // 获取document对象
  const ownerDocument = getOwnerDocumentFromRootContainer(
    rootContainerInstance,
  );
  switch (type) {
    case 'html': {
      const documentElement = ownerDocument.documentElement;
      if (!documentElement) {
        throw new Error(
          'React expected an <html> element (document.documentElement) to exist in the Document but one was' +
            ' not found. React never removes the documentElement for any Document it renders into so' +
            ' the cause is likely in some other script running on this page.',
        );
      }
      return documentElement;
    }
    case 'head': {
      const head = ownerDocument.head;
      if (!head) {
        throw new Error(
          'React expected a <head> element (document.head) to exist in the Document but one was' +
            ' not found. React never removes the head for any Document it renders into so' +
            ' the cause is likely in some other script running on this page.',
        );
      }
      return head;
    }
    case 'body': {
      const body = ownerDocument.body;
      if (!body) {
        throw new Error(
          'React expected a <body> element (document.body) to exist in the Document but one was' +
            ' not found. React never removes the body for any Document it renders into so' +
            ' the cause is likely in some other script running on this page.',
        );
      }
      return body;
    }
    default: {
      throw new Error(
        'resolveSingletonInstance was called with an element type that is not supported. This is a bug in React.',
      );
    }
  }
}

<Tag>HostComponent

React 处理 HostComponent(如 DOM 元素)的核心逻辑,主要负责创建或更新实际的 DOM 节点。它根据节点是否已存在、是否来自服务端渲染水合等情况,选择不同的处理路径,并处理特殊属性(如自动聚焦)和预加载逻辑。

TypeScript 复制代码
case HostComponent: {
  // 退出当前宿主环境上下文栈
  popHostContext(workInProgress);
  // 获取组件类型,类如div,span
  const type = workInProgress.type;
  // 更新已存在的 DOM 节点
  if (current !== null && workInProgress.stateNode != null) {
    // 调用 updateHostComponent 更新 DOM 属性(如 className、style)。处理事件监听器的添加 / 移除。
    updateHostComponent(
      current,
      workInProgress,
      type,
      newProps,
      renderLanes,
    );

    // 首次渲染或新增
  } else {
    if (!newProps) {
      // 错误处理:新节点必须有 props
      if (workInProgress.stateNode === null) {
        throw new Error(
          'We must have new props for new mounts. This error is likely ' +
            'caused by a bug in React. Please file an issue.',
        );
      }

      // This can happen when we abort work.
      bubbleProperties(workInProgress);
      return null;
    }

    const currentHostContext = getHostContext();
 
    const wasHydrated = popHydrationState(workInProgress);
    if (wasHydrated) {
      // prepareToHydrateHostInstance(workInProgress, currentHostContext);
    } else {
      // 纯客户端渲染逻辑
      
      const rootContainerInstance = getRootHostContainer();
      // 调用 createInstance 创建新 DOM 元素。
      const instance = createInstance(
        type,
        newProps,
        rootContainerInstance,
        currentHostContext,
        workInProgress,
      );
    
      markCloned(workInProgress);
      // 通过 appendAllChildren 添加子元素。
      appendAllChildren(instance, workInProgress, false, false);
      workInProgress.stateNode = instance;

      if (
        finalizeInitialChildren(
          instance,
          type,
          newProps,
          currentHostContext,
        )
      ) {
        markUpdate(workInProgress);
      }
    }
  }
   // 属性冒泡
  bubbleProperties(workInProgress);

  // 预加载
  preloadInstanceAndSuspendIfNeeded(
    workInProgress,
    workInProgress.type,
    workInProgress.pendingProps,
    renderLanes,
  );
  return null;
}

updateHostComponent

updateHostComponent 是 React 处理宿主组件(如 DOM 元素)更新的核心函数,根据渲染模式(突变模式或持久化模式)选择不同的更新策略。在突变模式下,直接标记节点更新;在持久化模式下,通过克隆实例实现无突变更新,并支持状态保留。

javascript 复制代码
function updateHostComponent(
  current: Fiber,
  workInProgress: Fiber,
  type: Type,
  newProps: Props,
  renderLanes: Lanes,
) {
  // 突变模式下的处理(supportsMutation)
  if (supportsMutation) {
  
    const oldProps = current.memoizedProps;
    // 比较新旧属性引用是否相同(浅比较)。
    if (oldProps === newProps) {
      return;
    }
    // 标记节点为更新状态
    markUpdate(workInProgress);

    // 持久化模式下的处理(supportsPersistence)
  } else if (supportsPersistence) {
    const currentInstance = current.stateNode;
    const oldProps = current.memoizedProps;
    
    // 通过 doesRequireClone 检查节点是否需要克隆。
    const requiresClone = doesRequireClone(current, workInProgress);
    if (!requiresClone && oldProps === newProps) {
      workInProgress.stateNode = currentInstance;
      return;
    }
    const currentHostContext = getHostContext();

    let newChildSet = null;
    if (requiresClone && passChildrenWhenCloningPersistedNodes) {
      markCloned(workInProgress);
      newChildSet = createContainerChildSet();
      // If children might have changed, we have to add them all to the set.
      appendAllChildrenToContainer(
        newChildSet,
        workInProgress,
        /* needsVisibilityToggle */ false,
        /* isHidden */ false,
      );
    }

    // 调用 cloneInstance 创建新实例,保留旧状态。
    const newInstance = cloneInstance(
      currentInstance,
      type,
      oldProps,
      newProps,
      !requiresClone,
      newChildSet,
    );
    if (newInstance === currentInstance) {
      // 无需变更,复用
      workInProgress.stateNode = currentInstance;
      return;
    } else {
      // 标记为克隆节点
      markCloned(workInProgress);
    }

    // 处理特殊属性(如自动聚焦)
    if (
      finalizeInitialChildren(newInstance, type, newProps, currentHostContext)
    ) {
      markUpdate(workInProgress);
    }

    // 更新子节点
    workInProgress.stateNode = newInstance;
    if (!requiresClone) {
      if (!enablePersistedModeClonedFlag) {
        markUpdate(workInProgress);
      }
    } else if (!passChildrenWhenCloningPersistedNodes) {
      // If children have changed, we have to add them all to the set.
      appendAllChildren(
        newInstance,
        workInProgress,
        /* needsVisibilityToggle */ false,
        /* isHidden */ false,
      );
    }
  }
}

createInstance

createInstance 方法,用于创建虚拟 DOM 元素的实例。该方法生成一个包含元素类型、属性和子节点信息的对象,并处理特殊属性(如 hiddensrc)和文本内容。通过不可枚举属性存储内部状态,确保这些信息不会暴露给外部代码。

javascript 复制代码
const sharedHostConfig = {
  createInstance(
    type: string,//元素的类型
    props: Props,// 传递给元素的属性对象。
    rootContainerInstance: Container,// 根容器实例,即渲染的目标容器。
    hostContext: HostContext,// 当前渲染的宿主环境上下文。
    internalInstanceHandle: Object,// 指向 React 内部 Fiber 节点的引用。
  ): Instance {
    if (type === 'errorInCompletePhase') {
      throw new Error('Error in host config.');
    }
    // 实例结构
    const inst = {
      id: instanceCounter++,
      type: type,
      children: [],
      parent: -1,
      // shouldSetTextContent:判断是否应将子节点作为文本内容处理。
      text: shouldSetTextContent(type, props)
        ? // eslint-disable-next-line react-internal/safe-string-coercion
        // computeText:处理文本内容(可能包括转义、格式化等)。
          computeText((props.children: any) + '', hostContext)
        : null,
      prop: props.prop,
      hidden: !!props.hidden,
      context: hostContext,
    };

    // 为 suspensey-thing 类型的元素添加 src 属性。
    if (type === 'suspensey-thing' && typeof props.src === 'string') {
      inst.src = props.src;
    }

    // Hide from unit tests
    // 将 id、parent、text 等属性设为不可枚举。
    // 防止这些内部状态在 for...in 循环或 JSON.stringify 中暴露。
    Object.defineProperty(inst, 'id', {value: inst.id, enumerable: false});
    Object.defineProperty(inst, 'parent', {
      value: inst.parent,
      enumerable: false,
    });
    Object.defineProperty(inst, 'text', {
      value: inst.text,
      enumerable: false,
    });
    Object.defineProperty(inst, 'context', {
      value: inst.context,
      enumerable: false,
    });
    Object.defineProperty(inst, 'fiber', {
      value: internalInstanceHandle,
      enumerable: false,
    });
    return inst;
  },
}

关键属性

  • id:唯一标识,用于内部跟踪。
  • type:元素类型(如 divspan)。
  • children:子节点数组。
  • text:文本内容(如果适用)。
  • hidden:是否隐藏(基于 props.hidden)。
javascript 复制代码
const inst = {
  id: instanceCounter++,
  type: type,
  children: [],
  parent: -1,
  // shouldSetTextContent:判断是否应将子节点作为文本内容处理。
  text: shouldSetTextContent(type, props)
    ? // eslint-disable-next-line react-internal/safe-string-coercion
    // computeText:处理文本内容(可能包括转义、格式化等)。
      computeText((props.children: any) + '', hostContext)
    : null,
  prop: props.prop,
  hidden: !!props.hidden,
  context: hostContext,
};

工具函数之 shouldSetTextContent

javascript 复制代码
function shouldSetTextContent(type: string, props: Props): boolean {
  if (type === 'errorInBeginPhase') {
    throw new Error('Error in host config.');
  }
  return (
    typeof props.children === 'string' ||
    typeof props.children === 'number' ||
    typeof props.children === 'bigint'
  );
}

工具函数之 computeText

computeText 是一个文本处理函数,用于根据宿主环境上下文决定是否转换文本大小写。当上下文为 UPPERCASE_CONTEXT 时,将文本转换为大写;否则保持原文。

javascript 复制代码
function computeText(rawText, hostContext) {
  return hostContext === UPPERCASE_CONTEXT ? rawText.toUpperCase() : rawText;
}

const UPPERCASE_CONTEXT = {};

工具函数之 finalizeInitialChildren

finalizeInitialChildren 函数主要用于完成元素初始化后的最终处理。

它执行以下核心操作:

  1. 通过 setInitialProperties 设置元素的初始属性(如 classNamestyle)。
  2. 根据元素类型判断是否需要在渲染后执行副作用操作(如自动聚焦、图片预加载)。
  3. 返回布尔值,指示该元素是否需要在提交阶段(commit phase)执行额外的副作用。
javascript 复制代码
function finalizeInitialChildren(
  domElement: Instance,
  type: string,
  props: Props,
  hostContext: HostContext,
): boolean {
  setInitialProperties(domElement, type, props);
  switch (type) {
    case 'button':
    case 'input':
    case 'select':
    case 'textarea':
      return !!props.autoFocus;
    case 'img':
      return true;
    default:
      return false;
  }
}

工具函数之 appendAllChildren

appendAllChildren 函数用于将 Fiber 树中的所有子节点挂载到父 DOM 节点上。它通过深度优先遍历 Fiber 树,将真实 DOM 节点(如 HostComponentHostText)按正确顺序添加到父容器中。该函数针对不同渲染模式(突变模式和持久化模式)进行了优化。

处理过程:

一、突变模式

  1. 处理当前子节点
    1. 如果节点tag是 HostComponent 或者 HostText,直接将 HostComponent(DOM 元素)或 HostText(文本节点)直接添加到父容器。
    2. 如果节点tag是 HostPortal,不做处理。(Portal 节点不直接添加到父容器,而是通过 Portal 机制挂载到其他容器)
    3. 如果当前子节点有子节点,则进入递归处理(则为下面的第2点)
  2. 递归处理子节点
  3. 如果当前子节点正是正在处理的fiber(workInProgress),则退出当前循环。
  4. 回溯或处理兄弟节点

二、持久化模式

  1. 处理当前子节点
    1. 如果节点tag是 HostComponent ,当需要隐藏时克隆节点并应用隐藏样式直接,然后将克隆实例 instance 直接添加到父容器。
    2. 如果节点tag是 HostText ,当需要隐藏时克隆节点并应用隐藏样式直接,然后将克隆实例 instance 直接添加到父容器。
    3. 如果节点tag是 HostPortal,不做处理。(Portal 节点不直接添加到父容器,而是通过 Portal 机制挂载到其他容器)
    4. 如果节点tag是 OffscreenComponent,处理其子节点与子节点的引用关系,调用appendAllChildren递归处理子节点。
    5. 如果当前子节点有子节点,则进入递归处理(则为下面的第2点)
  2. 递归处理子节点
  3. 如果当前子节点正是正在处理的fiber(workInProgress),则退出当前循环。
  4. 回溯或处理兄弟节点
javascript 复制代码
function appendAllChildren(
  parent: Instance,// 指定子节点要挂载到的父容器。
  workInProgress: Fiber,// 当前正在处理的 Fiber 节点,作为遍历起点。
  needsVisibilityToggle: boolean, //指示是否需要根据 isHidden 参数调整子节点的可见性。
  isHidden: boolean, // 当 needsVisibilityToggle 为 true 时,决定子节点是否应被隐藏。
) {

  // 突变模式处理(supportsMutation)
  if (supportsMutation) {
  
    let node = workInProgress.child;
    while (node !== null) {
       // 处理当前节点
      if (node.tag === HostComponent || node.tag === HostText) {
        // 将 HostComponent(DOM 元素)或 HostText(文本节点)直接添加到父容器。
        appendInitialChild(parent, node.stateNode);
        
      } else if (
        node.tag === HostPortal ||
        (supportsSingletons ? node.tag === HostSingleton : false)
      ) {
         // Portal 节点不直接添加到父容器,而是通过 Portal 机制挂载到其他容器

        // 递归处理子节点
      } else if (node.child !== null) {
        node.child.return = node;
        node = node.child;
        continue;
      }
      
      if (node === workInProgress) {
        return;
      }
      
      //  回溯或处理兄弟节点
      while (node.sibling === null) {
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        node = node.return;
      }
      
      node.sibling.return = node.return;
      node = node.sibling;
    }

    // 持久化模式处理(supportsPersistence)
  } else if (supportsPersistence) {
    // We only have the top Fiber that was created but we need recurse down its
    // children to find all the terminal nodes.
    let node = workInProgress.child;
    while (node !== null) {
      
      if (node.tag === HostComponent) {
        let instance = node.stateNode;
        if (needsVisibilityToggle && isHidden) {
          // 当节点位于超时的 Suspense 边界内时,克隆节点并应用隐藏样式。
          const props = node.memoizedProps;
          const type = node.type;
          instance = cloneHiddenInstance(instance, type, props);
        }
        //将 instance 直接添加到父容器。
        appendInitialChild(parent, instance);
        
      } else if (node.tag === HostText) {
        let instance = node.stateNode;
        
        if (needsVisibilityToggle && isHidden) {
          const text = node.memoizedProps;
          instance = cloneHiddenTextInstance(instance, text);
        }
        
        appendInitialChild(parent, instance);

        
      } else if (node.tag === HostPortal) {
         // Portal 节点不直接添加到父容器,而是通过 Portal 机制挂载到其他容器
      
      } else if (
        node.tag === OffscreenComponent &&
        node.memoizedState !== null
      ) {
   
        const child = node.child;
        if (child !== null) {
          child.return = node;
        }
        appendAllChildren(
          parent,
          node,
          /* needsVisibilityToggle */ true,
          /* isHidden */ true,
        );

        // 递归处理子节点
      } else if (node.child !== null) {
        node.child.return = node;
        node = node.child;
        continue;
      }
      if (node === workInProgress) {
        return;
      }

      // 回溯或处理兄弟节点
      while (node.sibling === null) {
       
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        node = node.return;
      }
     
      node.sibling.return = node.return;
      node = node.sibling;
    }
  }
}

工具函数之 appendInitialChild

javascript 复制代码
function appendInitialChild(
  parentInstance: Instance,
  child: Instance | TextInstance,
): void {
  parentInstance.appendChild(child);
}

工具函数之 cloneHiddenInstance

cloneHiddenInstance 方法,用于创建一个隐藏版本的 DOM 元素实例。该方法在需要临时隐藏元素但保留其结构和状态时使用。

javascript 复制代码
const hostConfig = useMutation ? {       
  cloneHiddenInstance(
      instance: Instance,
      type: string,
      props: Props,
    ): Instance {
      const clone = cloneInstance(instance, type, props, props, true, null);
      clone.hidden = true;
      return clone;
    },
}

工具函数之 cloneInstance

cloneInstance 是 React 内部用于克隆 DOM 元素实例的函数,主要用于在不改变原始实例的情况下创建一个具有新属性的副本

javascript 复制代码
function cloneInstance(
  instance: Instance,
  type: string,
  oldProps: Props,
  newProps: Props,
  keepChildren: boolean,
  children: ?$ReadOnlyArray<Instance>,
): Instance {

  // 实例克隆核心逻辑
  const clone = {
    id: instance.id,
    type: type,
    parent: instance.parent,
    children: keepChildren ? instance.children : children ?? [],
    text: shouldSetTextContent(type, newProps)
      ? computeText((newProps.children: any) + '', instance.context)
      : null,
    prop: newProps.prop,
    hidden: !!newProps.hidden,
    context: instance.context,
  };

  // 当元素类型为 suspensey-thing 且新属性包含 src 时,添加 src 属性到克隆实例。
  if (type === 'suspensey-thing' && typeof newProps.src === 'string') {
    clone.src = newProps.src;
  }

  // 不可枚举属性设置
  // 将关键内部属性设为不可枚举,避免在遍历或序列化时暴露。
// 保持与原始实例的行为一致性。
  Object.defineProperty(clone, 'id', {
    value: clone.id,
    enumerable: false,
  });
  Object.defineProperty(clone, 'parent', {
    value: clone.parent,
    enumerable: false,
  });
  Object.defineProperty(clone, 'text', {
    value: clone.text,
    enumerable: false,
  });
  Object.defineProperty(clone, 'context', {
    value: clone.context,
    enumerable: false,
  });
  hostCloneCounter++;
  return clone;
}

工具函数之 cloneHiddenTextInstance

cloneHiddenTextInstance 方法,专门用于克隆文本节点并将其标记为隐藏状态。用在处理需要临时隐藏文本内容但保留其结构和上下文的场景时。

javascript 复制代码
const hostConfig = useMutation ?  {
  cloneHiddenTextInstance(
    instance: TextInstance,
    text: string,
  ): TextInstance {
    const clone = {
      text: instance.text,
      id: instance.id,
      parent: instance.parent,
      hidden: true,
      context: instance.context,
    };
    // Hide from unit tests
    Object.defineProperty(clone, 'id', {
      value: clone.id,
      enumerable: false,
    });
    Object.defineProperty(clone, 'parent', {
      value: clone.parent,
      enumerable: false,
    });
    Object.defineProperty(clone, 'context', {
      value: clone.context,
      enumerable: false,
    });
    return clone;
  },
}

preloadInstanceAndSuspendIfNeeded

preloadInstanceAndSuspendIfNeeded 函数负责在元素实例化阶段预加载资源并决定是否需要挂起渲染。

它通过以下步骤实现精细控制:

  1. 判断元素是否可能触发提交阶段的挂起(如包含异步资源)。
  2. 尝试预加载资源并检查就绪状态。
  3. 根据资源状态和屏幕过渡策略,决定是继续渲染、挂起并保持当前屏幕,还是立即挂起并显示 fallback。
javascript 复制代码
function preloadInstanceAndSuspendIfNeeded(
  workInProgress: Fiber,
  type: Type,
  props: Props,
  renderLanes: Lanes,
) {
  if (!maySuspendCommit(type, props)) {
  // MaySuspendCommit:表示该 Fiber 节点可能会暂停提交操作。在 React 的并发模式下,渲染过程可能会被暂停和恢复
    workInProgress.flags &= ~MaySuspendCommit;
    return;
  }

  workInProgress.flags |= MaySuspendCommit;

  const isReady = preloadInstance(type, props);
  
  if (!isReady) {
    if (shouldRemainOnPreviousScreen()) {
      workInProgress.flags |= ShouldSuspendCommit;
    } else {
      suspendCommit();
    }
  }
}

工具函数之 preloadResource

preloadResource 是一个用于资源预加载控制的函数,主要决定特定资源是否需要暂停渲染(suspend)。核心逻辑是:当资源类型为样式表(stylesheet)且处于未加载状态时,函数返回 false 触发渲染暂停;否则返回 true 继续渲染。

javascript 复制代码
function preloadResource(resource: Resource): boolean {
  if (
    resource.type === 'stylesheet' &&
    (resource.state.loading & Settled) === NotLoaded
  ) {
    // Return false to indicate this resource should suspend
    return false;
  }

  // Return true to indicate this resource should not suspend
  return true;
}

<Tag>HostText

React 处理 HostText 类型节点(即文本节点)的核心逻辑,主要负责更新或创建文本节点实例。它会根据节点是否已存在、是否来自服务端渲染水合等情况,选择不同的处理路径,确保文本内容正确地反映到 DOM 中。

TypeScript 复制代码
case HostText: {

  // 获取新旧文本内容
  const newText = newProps;

  // 更新
  if (current && workInProgress.stateNode != null) {
    const oldText = current.memoizedProps;
    // 调用 updateHostText 更新现有文本节点内容(如 setTextContent)。
    updateHostText(current, workInProgress, oldText, newText);

    // 首次渲染
  } else {
    // 处理新创建的文本节点
    
    if (typeof newText !== 'string') {
      if (workInProgress.stateNode === null) {
        throw new Error(
          'We must have new props for new mounts. This error is likely ' +
            'caused by a bug in React. Please file an issue.',
        );
      }
      // This can happen when we abort work.
    }
    // 获取当前渲染的根容器(如 DOM 中的 root 节点)。
    const rootContainerInstance = getRootHostContainer();
    // 获取宿主环境上下文
    const currentHostContext = getHostContext();
    // 处理服务端渲染水合状态
    const wasHydrated = popHydrationState(workInProgress);

    // 检查当前节点是否来自服务端渲染的 HTML。
    if (wasHydrated) {
      // prepareToHydrateHostTextInstance(workInProgress);
    } else {
      // 添加Cloned标记
      markCloned(workInProgress);
      // 创建文本实例
      workInProgress.stateNode = createTextInstance(
        newText,
        rootContainerInstance,
        currentHostContext,
        workInProgress,
      );
    }
  }
  
  // 冒泡属性与完成处理
  bubbleProperties(workInProgress);
  return null;
}

updateHostText

updateHostText 是 React 处理文本节点更新的核心函数,根据渲染模式(突变模式或持久化模式)选择不同的更新策略。当文本内容发生变化时,突变模式直接标记更新,而持久化模式则创建新的文本实例并标记克隆。这一机制确保了在不同渲染模式下文本更新的高效性和正确性。

TypeScript 复制代码
function updateHostText(
  current: Fiber,
  workInProgress: Fiber,
  oldText: string,
  newText: string,
) {
  // 突变模式下的处理(supportsMutation)
  if (supportsMutation) {
    // If the text differs, mark it as an update. All the work in done in commitWork.
    // 当新旧文本内容不一致时,标记节点为 Update。
    if (oldText !== newText) {
      markUpdate(workInProgress);
    }

    // 持久化模式下的处理(supportsPersistence)
  } else if (supportsPersistence) {
    if (oldText !== newText) {
      // If the text content differs, we'll create a new text instance for it.
      const rootContainerInstance = getRootHostContainer();
      const currentHostContext = getHostContext();
      markCloned(workInProgress);
      // 创建新的文本实例
      workInProgress.stateNode = createTextInstance(
        newText,
        rootContainerInstance,
        currentHostContext,
        workInProgress,
      );
      
       // 确保父节点知道子节点已变更
      if (!enablePersistedModeClonedFlag) {
        // We'll have to mark it as having an effect, even though we won't use the effect for anything.
        // This lets the parents know that at least one of their children has changed.
        markUpdate(workInProgress);
      }
    } else {
       // 文本未变化,复用现有实例
      workInProgress.stateNode = current.stateNode;
    }
  }
}

工具函数之 markUpdate

javascript 复制代码
function markUpdate(workInProgress: Fiber) {
  workInProgress.flags |= Update;
}

工具函数之 markCloned

javascript 复制代码
function markCloned(workInProgress: Fiber) {
  if (supportsPersistence && enablePersistedModeClonedFlag) {
    // 添加 Cloned 标志(flags |= Cloned)。
    workInProgress.flags |= Cloned;
  }
}

工具函数之 getRootHostContainer

getRootHostContainer 是 React 渲染系统中的基础函数,用于获取当前渲染上下文的根宿主容器。在浏览器环境中,这通常对应于 ReactDOM.renderReactDOM.createRoot 指定的 DOM 节点(如 <div id="root"></div>)。

javascript 复制代码
function getRootHostContainer(): Container {
  const rootInstance = requiredContext(rootInstanceStackCursor.current);
  return rootInstance;
}
javascript 复制代码
const rootInstanceStackCursor: StackCursor<Container | null> = createCursor(null);
javascript 复制代码
function createCursor<T>(defaultValue: T): StackCursor<T> {
  return {
    current: defaultValue,
  };
}
javascript 复制代码
function requiredContext<Value>(c: Value | null): Value {
  return (c: any);
}

工具函数之 getHostContext

getHostContext() 是 React 渲染系统中的核心函数,用于获取当前宿主环境的上下文信息。这些信息包括命名空间(如 HTML/SVG)、样式作用域、容器配置等,是正确创建和渲染 DOM 元素的关键依据。通过维护一个上下文栈,React 能够在嵌套渲染(如 SVG 内部元素)时动态切换环境配置,确保元素正确渲染。

javascript 复制代码
function getHostContext(): HostContext {
  const context = requiredContext(contextStackCursor.current);
  return context;
}
javascript 复制代码
const contextStackCursor: StackCursor<Object> =
  createCursor(emptyContextObject);

const emptyContextObject: {} = {};

createTextInstance

createTextInstance 是 React 渲染系统中用于创建文本节点的核心函数。它通过宿主环境(如浏览器 DOM)提供的 API 创建实际的文本节点,并建立该节点与 React 内部 Fiber 节点的映射关系。这一过程是将虚拟 DOM 转换为真实 DOM 的关键步骤

TypeScript 复制代码
function createTextInstance(
  text: string,
  rootContainerInstance: Container,
  hostContext: HostContext,
  internalInstanceHandle: Object,
): TextInstance {
  // 创建新文本节点,调用 document.createTextNode(text) 创建实际的文本节点。
  const textNode: TextInstance = getOwnerDocumentFromRootContainer(
    rootContainerInstance,
  ).createTextNode(text);
  
   // 建立 Fiber 节点与 DOM 节点的映射
  precacheFiberNode(internalInstanceHandle, textNode);
  
  return textNode;
}

工具函数之 getOwnerDocumentFromRootContainer

获取根容器所属的 document 对象。

javascript 复制代码
function getOwnerDocumentFromRootContainer(
  rootContainerElement: Element | Document | DocumentFragment,
): Document {
  return rootContainerElement.nodeType === DOCUMENT_NODE
    ? (rootContainerElement: any)
    : rootContainerElement.ownerDocument;
}

工具函数之 precacheFiberNode

建立 Fiber 节点与 DOM 节点的映射

javascript 复制代码
function precacheFiberNode(
  hostInst: Fiber,
  node: Instance | TextInstance | SuspenseInstance | ReactScopeInstance,
): void {
  (node: any)[internalInstanceKey] = hostInst;
}
javascript 复制代码
const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;

<Tag>HostPortal

处理 React 中 HostPortal 类型的 Fiber 节点,用于管理通过 ReactDOM.createPortal 创建的 Portal 组件。Portal 允许将子组件渲染到 DOM 树的不同位置(如独立的模态框、悬浮层),但保持与父组件的上下文关系。

执行过程:

  1. 弹出 Portal 容器的上下文
  2. 更新 Portal 容器的状态
  3. 准备 Portal 挂载(首次渲染时)
  4. 冒泡属性以传播副作用和状态
TypeScript 复制代码
case HostPortal:
// 从上下文栈中弹出当前 Portal 的容器信息。
  popHostContainer(workInProgress);

// 比较新旧 Portal 容器的属性差异。标记需要执行的副作用(如属性更新、事件绑定)。
  updateHostContainer(current, workInProgress);

// 首次挂载处理
  if (current === null) {
    //确保目标容器存在且可访问。注册 Portal 相关的事件处理(如点击穿透)。应用初始样式或动画。
    preparePortalMount(workInProgress.stateNode.containerInfo);
  }

// 属性冒泡,将 Portal 子树的状态(如副作用标志、优先级)传播到父节点。
  bubbleProperties(workInProgress);
  return null;

工具函数之 preparePortalMount

javascript 复制代码
function preparePortalMount(portalInstance: Instance): void {
  listenToAllSupportedEvents(portalInstance);
}

工具函数之 listenToAllSupportedEvents

主要功能是为 React 应用的指定容器添加对所有支持的原生事件的监听。它会对事件监听进行去重处理,避免重复添加相同的事件监听器。同时,对于 selectionchange 事件会进行特殊处理,因为该事件不会冒泡,需要绑定到 document 对象上。 (详细的后续写一篇记录react合成事件)

javascript 复制代码
function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
  // 检查 rootContainerElement 是否已经添加了事件监听器。如果没有,则将 listeningMarker 作为属性添加到 rootContainerElement 上,并将其值设为 true,表示已经添加了事件监听器。
  if (!(rootContainerElement: any)[listeningMarker]) {
    (rootContainerElement: any)[listeningMarker] = true;

    // allNativeEvents 是一个集合,包含了 React 支持的所有原生事件。
    // 遍历所有支持的原生事件
    allNativeEvents.forEach(domEventName => {
      if (domEventName !== 'selectionchange') {
        if (!nonDelegatedEvents.has(domEventName)) {
          listenToNativeEvent(domEventName, false, rootContainerElement);
        }
        listenToNativeEvent(domEventName, true, rootContainerElement);
      }
    });

    // 获取顶层document对象
    const ownerDocument =
      (rootContainerElement: any).nodeType === DOCUMENT_NODE
      ? rootContainerElement
      : (rootContainerElement: any).ownerDocument;
    
    if (ownerDocument !== null) {
      // The selectionchange event also needs deduplication
      // but it is attached to the document.
      if (!(ownerDocument: any)[listeningMarker]) {
        (ownerDocument: any)[listeningMarker] = true;
        listenToNativeEvent('selectionchange', false, ownerDocument);
      }
    }

  }
}

全局常量(根节点状态)

React 渲染根节点(Root)的状态机,用于表示整个渲染过程中可能处于的不同阶段和结果。

javascript 复制代码
type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;
const RootInProgress = 0; // 根节点正在进行渲染
const RootFatalErrored = 1; // 渲染过程中发生致命错误,无法恢复。
const RootErrored = 2; //渲染过程中发生可恢复的错误。
const RootSuspended = 3; // 渲染被挂起,等待异步资源(如数据或代码)。
const RootSuspendedWithDelay = 4; // 渲染被挂起,但设置了延迟显示加载状态。
const RootCompleted = 5; // 渲染成功完成,所有工作已提交到 DOM。
const RootDidNotComplete = 6; // 渲染未完成,可能被更高优先级的更新中断。

全局常量(组件类型)

React 内部用于标识不同类型组件和节点的常量(称为 FiberTag)。每个常量对应一种特定的组件或节点类型。

javascript 复制代码
export const FunctionComponent = 0;
export const ClassComponent = 1;

export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;

export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;

export const SuspenseComponent = 13;

export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;

export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;

export const SuspenseListComponent = 19;
export const ScopeComponent = 21;
export const OffscreenComponent = 22;
export const LegacyHiddenComponent = 23;
export const CacheComponent = 24;
export const TracingMarkerComponent = 25;

export const HostHoistable = 26;
export const HostSingleton = 27;

export const IncompleteFunctionComponent = 28;
export const Throw = 29;

全局常量(节点的状态和操作位掩码)

标记节点状态和操作的位掩码常量(Flags)。每个标志都是一个二进制值,通过位运算可以高效地组合、检查和修改这些标志。

javascript 复制代码
// 无操作(初始状态)。
const NoFlags = /*                      */ 0b0000000000000000000000000000;
// 组件已执行工作(用于优化)。
const PerformedWork = /*                */ 0b0000000000000000000000000001;
// 需要插入或移动 DOM 节点。
const Placement = /*                    */ 0b0000000000000000000000000010;
// 组件捕获到错误(用于错误边界)。
const DidCapture = /*                   */ 0b0000000000000000000010000000;
// 需要重置节点内容(如文本节点内容完全改变)。
const Hydrating = /*                    */ 0b0000000000000001000000000000;

// You can change the rest (and add more).
// 需要更新 DOM 节点属性(如 props 变化)。
const Update = /*                       */ 0b0000000000000000000000000100;
// Fiber 被克隆(如列表重排序时复用节点)。
const Cloned = /*                       */ 0b0000000000000000000000001000;

// 需要删除子节点。
const ChildDeletion = /*                */ 0b0000000000000000000000010000;
// 需要重置节点内容(如文本节点内容完全改变)。
const ContentReset = /*                 */ 0b0000000000000000000000100000;
// 需要执行回调(如 useEffect、useLayoutEffect)。
const Callback = /*                     */ 0b0000000000000000000001000000;
/* Used by DidCapture:                            0b0000000000000000000010000000; */

// 强制客户端渲染(如 SSR 水合失败)。
 const ForceClientRender = /*            */ 0b0000000000000000000100000000;
// 需要处理 ref 关联或解关联。
 const Ref = /*                          */ 0b0000000000000000001000000000;
// 需要捕获 DOM 快照(如 getSnapshotBeforeUpdate)。
 const Snapshot = /*                     */ 0b0000000000000000010000000000;
// 表示存在被动副作用(如 useEffect),需要异步执行。
 const Passive = /*                      */ 0b0000000000000000100000000000;
/* Used by Hydrating:                             0b0000000000000001000000000000; */

// 组件可见性变化(如 Suspense 显示 / 隐藏)。
 const Visibility = /*                   */ 0b0000000000000010000000000000;
// 状态一致性标记(用于并发模式)。
 const StoreConsistency = /*             */ 0b0000000000000100000000000000;

全局常量

  1. RefStatic:用于标记与 ref 相关的静态状态。在 React 中,ref 用于访问 DOM 节点或组件实例,这个标志可能表示 ref 相关的操作或状态是静态的,不需要在每次渲染时重新处理。
  2. LayoutStatic:表示与布局相关的静态状态。布局操作可能涉及到元素的位置、大小等信息,这个标志可能用于指示某些布局信息在渲染过程中是静态的,不需要频繁更新。
  3. PassiveStatic:与被动副作用相关的静态状态。被动副作用通常是指那些在渲染完成后异步执行的副作用,如 useEffect 钩子中的某些操作,这个标志可能表示这些副作用的状态是静态的。
  4. MaySuspendCommit:表示该 Fiber 节点可能会暂停提交操作。在 React 的并发模式下,渲染过程可能会被暂停和恢复,这个标志用于标记那些可能会导致提交操作暂停的节点。
javascript 复制代码
const StaticMask =
  LayoutStatic | PassiveStatic | RefStatic | MaySuspendCommit;


const RefStatic = /*                    */ 0b0000001000000000000000000000;
const LayoutStatic = /*                 */ 0b0000010000000000000000000000;
const PassiveStatic = /*                */ 0b0000100000000000000000000000;
const MaySuspendCommit = /*             */ 0b0001000000000000000000000000;
相关推荐
pianmian15 分钟前
3dczml时间动态图型场景
前端·javascript·数据库
编程大全19 分钟前
45道工程模块化高频题整理(附答案背诵版)
前端·工程化
君的名字25 分钟前
怎么判断一个Android APP使用了React Native 这个跨端框架
android·react native·react.js
好奇的菜鸟28 分钟前
如何升级 npm:从版本管理到最佳实践
前端·npm·node.js
iamtsfw28 分钟前
从头实现react native expo本地生成APK
javascript·react native·react.js
whatever who cares32 分钟前
react native搭建项目
react.js
伊成35 分钟前
扫盲笔记之NPM
前端·笔记·npm
coding随想1 小时前
JavaScript的三大核心组成:ECMAScript、DOM与BOM
开发语言·javascript·ecmascript
飞飞9871 小时前
spring mvc
java·服务器·前端
sql123456789111 小时前
Vue-js
前端·javascript·vue.js