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类型的资源

处理 外部样式表资源( **