《深入浅出 React 19:AI 视角下的源码解析与进阶》React 19更新机制详解

如果你对 React 源码解析感兴趣,欢迎访问我的个人博客:《深入浅出 React 19:AI 视角下的源码解析与进阶》 或者我的微信公众号- 前端小卒

在我的博客和公众号中,你可以找到:

🔍 完整的 React 源码解析电子书 - 从基础概念到高级实现,全面覆盖 React 19 的核心机制 📖 系统化的学习路径 - 按照 React 的执行流程,循序渐进地深入每个模块 💡 实战案例分析 - 结合真实场景,理解 React 设计思想和最佳实践 🚀 最新技术动态 - 持续更新 React 新特性和性能优化技巧

React 更新机制详解

在 React 中,任何能够引起 UI 变化的操作都被视为一次"更新"。从用户交互(如点击按钮)到 API 调用(如 setState),这些触发源最终都会被转化为一个标准的内部更新对象,并进入调度流程。本章节将详细拆解从更新触发到任务调度的全过程,揭示其如何与调度阶段(Scheduling Phase)紧密相连。

一、更新的触发源

React 的更新可以由多种事件触发,它们共同构成了驱动应用动态变化的基础。这些触发源可以大致分为以下几类:

1. 初始渲染

typescript 复制代码
// React 19.2.0 中的初始渲染
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root')!);
root.render(<App />); // 触发初始更新

2. 状态与 Props 变更

typescript 复制代码
interface CounterState {
  count: number;
}

const [count, setCount] = useState<number>(0);

// 同步更新
setCount(count + 1);

// 函数式更新(推荐)
setCount(prev => prev + 1);

// 批量更新(React 18+)
setTimeout(() => {
  setCount(c => c + 1); // 自动批处理
  setCount(c => c + 1);
}, 1000);

Props 变化

typescript 复制代码
interface ChildProps {
  value: number;
  onUpdate: (value: number) => void;
}

// 父组件重新渲染导致子组件 props 变化
function Parent() {
  const [state, setState] = useState(0);
  
  return (
    <ChildComponent 
      value={state} 
      onUpdate={setState}
    />
  );
}

Context 变化

typescript 复制代码
interface ThemeContextType {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | null>(null);

// Context Provider 值变化触发消费者更新
function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  
  const toggleTheme = useCallback(() => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  }, []);
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

3. React 19 新机制

Actions(异步状态管理)
typescript 复制代码
interface FormData {
  name: string;
  email: string;
}

function MyComponent() {
  const [isPending, startTransition] = useTransition();
  const [error, setError] = useState<string | null>(null);
  
  const handleSubmit = async (formData: FormData) => {
    try {
      startTransition(async () => {
        const result = await submitForm(formData);
        if (!result.success) {
          setError(result.error);
        }
      });
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
    }
  };
  
  return (
    <form action={handleSubmit}>
      {isPending && <div>Submitting...</div>}
      {error && <div>Error: {error}</div>}
      {/* form fields */}
    </form>
  );
}
use(Promise)(Suspense 集成)
typescript 复制代码
interface ApiResponse {
  data: any[];
  status: 'success' | 'error';
}

function DataComponent({ promise }: { promise: Promise<ApiResponse> }) {
  try {
    const result = use(promise); // Promise resolve 时触发更新
    
    if (result.status === 'error') {
      throw new Error('Failed to load data');
    }
    
    return (
      <div>
        {result.data.map(item => (
          <div key={item.id}>{item.name}</div>
        ))}
      </div>
    );
  } catch (error) {
    // 错误边界会捕获这个错误
    throw error;
  }
}
Server Components(服务端渲染优化)
typescript 复制代码
// 服务端组件(在服务器上运行)
async function ServerComponent() {
  try {
    const data = await fetchData();
    return <ClientComponent data={data} />;
  } catch (error) {
    console.error('Server component error:', error);
    return <ErrorFallback />;
  }
}

// 客户端组件
'use client';
function ClientComponent({ data }: { data: any[] }) {
  const [localState, setLocalState] = useState(data);
  
  useEffect(() => {
    // 客户端特定逻辑
    setLocalState(data);
  }, [data]);
  
  return (
    <div>
      {localState.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

4. 强制同步更新

typescript 复制代码
// flushSync:强制同步执行更新
import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCount(c => c + 1);
  });
  // 此时 DOM 已经同步更新
  console.log(document.getElementById('counter')?.textContent);
}

尽管触发源多种多样,但它们最终都会殊途同归,进入 React 内部统一的更新处理流程。

二、更新的创建与入队

无论更新源自何处,React 都会执行标准化的内部流程:创建 Update 对象 -> 计算优先级(Lane)-> 将 Update 入队

1. 创建 Update 对象

typescript 复制代码
// React 19.2.0 中的 Update 类型定义
interface Update<State> {
  eventTime: number;
  lane: Lane;
  tag: UpdateTag;
  payload: any;
  callback: (() => void) | null;
  next: Update<State> | null;
}

enum UpdateTag {
  UpdateState = 0,
  ReplaceState = 1,
  ForceUpdate = 2,
  CaptureUpdate = 3,
}

function createUpdate<State>(eventTime: number, lane: Lane): Update<State> {
  const update: Update<State> = {
    eventTime,
    lane,
    tag: UpdateTag.UpdateState,
    payload: null,
    callback: null,
    next: null,
  };
  
  if (__DEV__) {
    // 开发环境下的调试信息
    (update as any)._debugSource = new Error().stack;
  }
  
  return update;
}

2. Lane 的计算(优先级系统)

typescript 复制代码
// Lane 类型定义
type Lane = number;
type Lanes = number;

// 优先级常量
const SyncLane: Lane = 0b0000000000000000000000000000001;
const InputContinuousLane: Lane = 0b0000000000000000000000000000100;
const DefaultLane: Lane = 0b0000000000000000000000000010000;
const TransitionLane1: Lane = 0b0000000000000000000000001000000;

function requestUpdateLane(fiber: Fiber): Lane {
  const mode = fiber.mode;
  
  // 非并发模式直接返回同步 Lane
  if ((mode & ConcurrentMode) === NoMode) {
    return SyncLane;
  }
  
  // 检查是否在 Transition 中
  if (isTransition()) {
    const transition = getCurrentTransition();
    if (transition !== null) {
      return claimNextTransitionLane();
    }
  }
  
  // 根据当前事件优先级计算 Lane
  const eventPriority = getCurrentEventPriority();
  
  switch (eventPriority) {
    case DiscreteEventPriority:
      return SyncLane;
    case ContinuousEventPriority:
      return InputContinuousLane;
    case DefaultEventPriority:
    default:
      return DefaultLane;
  }
}

// 性能监控:Lane 使用统计
if (__DEV__) {
  let laneUsageStats: Record<string, number> = {};
  
  function trackLaneUsage(lane: Lane) {
    const laneName = getLaneName(lane);
    laneUsageStats[laneName] = (laneUsageStats[laneName] || 0) + 1;
  }
  
  function getLaneName(lane: Lane): string {
    if (lane === SyncLane) return 'SyncLane';
    if (lane === InputContinuousLane) return 'InputContinuousLane';
    if (lane === DefaultLane) return 'DefaultLane';
    return `TransitionLane_${lane.toString(2)}`;
  }
}

3. 入队 Update(环形链表结构)

typescript 复制代码
interface UpdateQueue<State> {
  baseState: State;
  firstBaseUpdate: Update<State> | null;
  lastBaseUpdate: Update<State> | null;
  shared: SharedQueue<State>;
  effects: Array<Update<State>> | null;
}

interface SharedQueue<State> {
  pending: Update<State> | null;
  interleaved: Update<State> | null;
  lanes: Lanes;
}

function enqueueUpdate<State>(
  fiber: Fiber,
  update: Update<State>,
  lane: Lane
): void {
  const updateQueue = fiber.updateQueue as UpdateQueue<State>;
  
  if (updateQueue === null) {
    // 组件已卸载
    if (__DEV__) {
      console.warn('Cannot update unmounted component');
    }
    return;
  }
  
  const sharedQueue = updateQueue.shared;
  
  // 标记 Lane
  markUpdateLaneFromFiberToRoot(fiber, lane);
  
  // 构建环形链表
  const pending = sharedQueue.pending;
  if (pending === null) {
    // 第一个更新,指向自己形成环
    update.next = update;
  } else {
    // 插入到环形链表中
    update.next = pending.next;
    pending.next = update;
  }
  
  // 更新 pending 指针
  sharedQueue.pending = update;
  
  // 性能监控
  if (__DEV__) {
    trackLaneUsage(lane);
    measureUpdateQueueLength(updateQueue);
  }
}

// 调试工具:测量更新队列长度
function measureUpdateQueueLength<State>(updateQueue: UpdateQueue<State>): number {
  const pending = updateQueue.shared.pending;
  if (pending === null) return 0;
  
  let count = 1;
  let current = pending.next;
  while (current !== pending) {
    count++;
    current = current!.next;
  }
  
  if (__DEV__ && count > 10) {
    console.warn(`Large update queue detected: ${count} updates pending`);
  }
  
  return count;
}

这个过程确保了所有更新都被规范化,并准备好进入下一步的调度环节。

三、调度入口:连接 Reconciler 与 Scheduler

更新入队后,需要触发调度。这个过程通过两个关键函数实现:

  1. scheduleUpdateOnFiber - Reconciler 的调度入口
  2. ensureRootIsScheduled - 连接到 Scheduler 的桥梁
flowchart TD A["更新触发 (setState/props变化)"] --> B["创建 Update 对象"] B --> C["enqueueUpdate 入队"] C --> D["scheduleUpdateOnFiber"] D --> E{"检查更新类型"} E -->|同步更新| F["flushSyncCallbacks"] E -->|并发更新| G["ensureRootIsScheduled"] G --> H{"检查现有任务"} H -->|有相同优先级任务| I["复用现有任务"] H -->|需要新任务| J["Scheduler.scheduleCallback"] J --> K["performConcurrentWorkOnRoot"] F --> L["performSyncWorkOnRoot"] I --> M["等待现有任务执行"] K --> N["Reconciler 开始工作"] L --> N M --> N N --> O{"是否有更高优先级更新"} O -->|是| P["中断当前工作"] O -->|否| Q["继续当前工作"] P --> G Q --> R["完成渲染"] style D fill:#e1f5fe style G fill:#f3e5f5 style J fill:#fff3e0 style O fill:#ffebee

这个流程体现了 React 架构的几个重要特点:

1. 职责分离

  • Reconciler 负责 Fiber 树的构建和 Diff 算法
  • Scheduler 负责任务的时间切片和优先级调度
  • scheduleUpdateOnFiberensureRootIsScheduled 作为连接层
  • Lane 系统 负责优先级管理和批处理

2. 关键桥梁

这两个函数是整个调度系统的关键桥梁:

  • 从应用层的状态更新(用户交互、数据变化)
  • 到底层的任务调度(时间切片、优先级队列)
  • 实现了优雅的解耦和可扩展性

3. 异步实现与性能优化

通过 Scheduler 实现了:

  • 时间切片(Time Slicing):将长任务分解为小块
  • 优先级调度(Priority Scheduling):高优先级任务优先执行
  • 可中断渲染(Interruptible Rendering):支持任务中断和恢复
  • 批处理优化(Batching):合并多个更新减少渲染次数
  • 并发特性(Concurrent Features):Suspense、Transitions 等

4. React 19 增强特性

  • 自动批处理:所有更新默认批处理,包括 setTimeout、Promise 等
  • Transition API:区分紧急和非紧急更新
  • Suspense 改进:更好的加载状态管理
  • Server Components:服务端渲染优化

四、scheduleUpdateOnFiber:调度的统一入口

scheduleUpdateOnFiber 是 React 调度系统的核心入口函数,负责处理 Fiber 节点上的更新调度。以下是移除开发代码后的核心实现和详细解析:

scheduleUpdateOnFiber 函数执行流程

  1. 标记更新 :调用 markRootUpdated 更新根节点的车道信息

  2. 渲染阶段检查

    • 如果在渲染阶段收到更新,标记渲染阶段更新车道
    • 否则进入正常调度流程
  3. 挂起状态处理

    • 检查根节点是否因数据延迟而挂起
    • 如果是,调用 prepareFreshStack 中断当前渲染
  4. 调度确保 :调用 ensureRootIsScheduled 确保根节点被调度

  5. 同步工作刷新

    • 在特定条件下(同步车道、无执行上下文、非并发模式等)
    • 立即刷新同步工作

::: details scheduleUpdateOnFiber 内部函数解析

javascript 复制代码
export function scheduleUpdateOnFiber(
  root: FiberRoot,
  fiber: Fiber,
  lane: Lane,
) {
  // 标记根节点已更新
  markRootUpdated(root, lane);

  // 检查是否在渲染阶段
  if (
    (executionContext & RenderContext) !== NoLanes &&
    root === workInProgressRoot
  ) {
    // 在渲染阶段收到更新,标记渲染阶段更新车道
    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(
      workInProgressRootRenderPhaseUpdatedLanes,
      lane,
    );
  } else {
    // 检查根节点是否因延迟而挂起
    if (
      workInProgressRoot === root &&
      workInProgressRootExitStatus === RootSuspendedWithDelay &&
      workInProgressSuspendedReason === SuspendedOnData
    ) {
      // 中断当前渲染并切换到新更新
      prepareFreshStack(root, NoLanes);
    }

    // 确保根节点被调度
    ensureRootIsScheduled(root);

    // 如果满足条件,立即刷新同步工作
    if (
      lane === SyncLane &&
      executionContext === NoContext &&
      (fiber.mode & ConcurrentMode) === NoMode &&
      !ReactCurrentActQueue.isBatchingLegacy
    ) {
      resetRenderTimer();
      flushSyncWorkOnLegacyRootsOnly();
    }
  }
}

:::

核心调用的重要函数解析

1. markRootUpdated(root, lane)

作用:标记根节点已更新,更新相关车道信息

javascript 复制代码
export function markRootUpdated(root: FiberRoot, updateLane: Lane) {
  // 将更新车道添加到待处理车道
  root.pendingLanes |= updateLane;
  
  // 标记可能需要显示加载指示器的车道
  if (enableDefaultTransitionIndicator) {
    root.indicatorLanes |= updateLane & TransitionLanes;
  }

  // 清除挂起的车道,因为新更新可能解除阻塞
  if (updateLane !== IdleLane) {
    root.suspendedLanes = NoLanes;
    root.pingedLanes = NoLanes;
    root.warmLanes = NoLanes;
  }
}
2. prepareFreshStack(root, lanes)

作用:准备新的工作栈,重置渲染状态

javascript 复制代码
function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
  // 取消之前的超时处理
  const timeoutHandle = root.timeoutHandle;
  if (timeoutHandle !== noTimeout) {
    root.timeoutHandle = noTimeout;
    cancelTimeout(timeoutHandle);
  }
  
  // 取消待处理的提交
  const cancelPendingCommit = root.cancelPendingCommit;
  if (cancelPendingCommit !== null) {
    root.cancelPendingCommit = null;
    cancelPendingCommit();
  }

  // 重置工作进度栈
  resetWorkInProgressStack();
  workInProgressRoot = root;
  
  // 创建新的工作进度节点
  const rootWorkInProgress = createWorkInProgress(root.current, null);
  workInProgress = rootWorkInProgress;
  
  // 重置所有工作进度相关状态
  workInProgressRootRenderLanes = lanes;
  workInProgressSuspendedReason = NotSuspended;
  workInProgressThrownValue = null;
  workInProgressRootDidSkipSuspendedSiblings = false;
  workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
  workInProgressRootDidAttachPingListener = false;
  workInProgressRootExitStatus = RootInProgress;
  workInProgressRootSkippedLanes = NoLanes;
  workInProgressRootInterleavedUpdatedLanes = NoLanes;
  workInProgressRootRenderPhaseUpdatedLanes = NoLanes;
  workInProgressRootPingedLanes = NoLanes;
  workInProgressDeferredLane = NoLane;
  workInProgressSuspendedRetryLanes = NoLanes;
  workInProgressRootConcurrentErrors = null;
  workInProgressRootRecoverableErrors = null;
  workInProgressRootDidIncludeRecursiveRenderUpdate = false;

  // 获取纠缠的渲染车道
  entangledRenderLanes = getEntangledLanes(root, lanes);
  
  // 完成并发更新队列
  finishQueueingConcurrentUpdates();

  return rootWorkInProgress;
}
3. ensureRootIsScheduled(root)

作用:确保根节点被调度(详见之前的解析)

这个函数是调度系统的核心,负责:

  • 将根节点添加到调度队列
  • 确保有微任务处理根调度
  • 处理同步工作刷新
4. flushSyncWorkOnLegacyRootsOnly()

作用:仅在旧版根节点上刷新同步工作

javascript 复制代码
function flushSyncWorkOnLegacyRootsOnly() {
  // 只处理旧版模式的根节点
  if (rootsWithPendingDiscreteUpdates !== null) {
    const roots = rootsWithPendingDiscreteUpdates;
    rootsWithPendingDiscreteUpdates = null;
    roots.forEach(root => {
      if ((root.mode & ConcurrentMode) === NoMode) {
        flushRoot(root, SyncLane);
      }
    });
  }
  flushSyncWorkAcrossRoots_impl(true);
}

五、ensureRootIsScheduled:调度阶段的统一入口

ensureRootIsScheduled 可以被视为 React 调度阶段的 最终统一入口 ,它负责将所有待处理的更新请求,以标准化的方式提交给底层的 Scheduler,从而启动或更新 React 的工作循环。这是 React 实现统一调度和优先级管理的关键所在。

::: details ensureRootIsScheduled函数

javascript 复制代码
function ensureRootIsScheduled(root: FiberRoot): void {
  // 1. 获取当前根节点的调度优先级 (Scheduler Priority)
  //    这个优先级是根据 root.pendingLanes 计算得出的,代表了当前根节点上所有待处理更新中最高的优先级。
  const currentSchedulerPriority = getHighestPriorityLane(root.pendingLanes)

  // 2. 如果当前根节点已经有一个调度任务,并且其优先级低于或等于新的调度优先级,
  //    则无需重新调度,直接返回。
  //    这避免了重复调度低优先级的任务。
  if (
    root.callbackNode !== null &&
    root.callbackPriority <= currentSchedulerPriority
  ) {
    return
  }

  // 3. 清除旧的调度任务(如果存在)
  //    如果需要重新调度(例如,有更高优先级的更新到来),则取消之前安排的调度任务。
  cancelCallback(root.callbackNode)

  // 4. 如果没有待处理的更新,则将 callbackNode 和 callbackPriority 重置为 null/NoLane,并返回。
  //    这意味着当前根节点没有需要处理的任务。
  if (currentSchedulerPriority === NoLane) {
    root.callbackNode = null
    root.callbackPriority = NoLane
    return
  }

  // 5. 根据 React 优先级计算出对应的调度器优先级 (Scheduler Priority)
  //    React 内部的 Lane 优先级需要映射到 Scheduler 模块理解的优先级。
  const schedulerPriority = reactPriorityToSchedulerPriority(
    currentSchedulerPriority,
  )

  // 6. 计算调度任务的过期时间 (Expiration Time)
  //    这决定了任务最迟何时必须执行,即使浏览器不空闲。
  const expirationTime = calculateExpirationTime(currentSchedulerPriority)

  // 7. 安排一个新的调度任务
  //    调用 Scheduler 模块的 `scheduleCallback` 函数,安排一个回调函数。
  //    这个回调函数通常是 `performSyncWorkOnRoot` 或 `performConcurrentWorkOnRoot`,
  //    它将在浏览器空闲时或过期时间到达时执行,从而启动协调阶段。
  root.callbackNode = scheduleCallback(
    schedulerPriority,
    performWorkOnRoot.bind(null, root),
    { timeout: expirationTime },
  )
  root.callbackPriority = currentSchedulerPriority
}

// 辅助函数(在实际代码中可能位于其他文件或作为内部实现)
function getHighestPriorityLane(lanes: Lanes): Lane {
  /* ... */
}
function cancelCallback(callbackNode: CallbackNode): void {
  /* ... */
}
function reactPriorityToSchedulerPriority(
  reactPriority: Lane,
): SchedulerPriority {
  /* ... */
}
function calculateExpirationTime(priority: Lane): Milliseconds {
  /* ... */
}
function scheduleCallback(
  priority: SchedulerPriority,
  callback: Function,
  options: { timeout: Milliseconds },
): CallbackNode {
  /* ... */
}
function performWorkOnRoot(
  root: FiberRoot,
  didTimeout: boolean,
): CallbackNode | null {
  /* ... */
}

:::

解析

packages/react-reconciler/src/ReactFiberRootScheduler.js 中的 ensureRootIsScheduled 函数是 React 调度阶段的核心入口之一,它的主要职责是根据当前 Fiber 根节点(FiberRoot)上待处理的更新(pendingLanes),决定是否需要向 Scheduler 模块安排一个新的调度任务,以及如何安排这个任务。

  1. 获取当前根节点的调度优先级 (currentSchedulerPriority)

    • getHighestPriorityLane(root.pendingLanes):这个函数会从 root.pendingLanes(一个位掩码,表示所有待处理更新的优先级集合)中,找出优先级最高的那个 Lane。这个 Lane 代表了当前根节点上最紧急的更新。
    • 目的:确定当前根节点需要处理的最高优先级任务是什么。
  2. 避免重复调度

    • if (root.callbackNode !== null && root.callbackPriority <= currentSchedulerPriority):这里进行了一个重要的优化检查。如果当前根节点已经有一个调度任务(root.callbackNode 不为 null),并且这个已存在的任务的优先级(root.callbackPriority)已经足够高(即小于或等于 currentSchedulerPriority),那么就没有必要再安排一个新的调度任务了。直接返回,避免不必要的 Scheduler 调用。
    • 目的:防止重复调度,提高效率。
  3. 清除旧的调度任务

    • cancelCallback(root.callbackNode):如果上述条件不满足(即没有调度任务,或者新来的更新优先级更高),那么就需要重新安排调度。在安排新任务之前,必须取消掉之前可能存在的旧的调度任务。这是因为新的任务可能具有更高的优先级,或者旧的任务已经不再需要。
    • 目的:确保只有一个有效的调度任务在 Scheduler 中排队,并且总是最高优先级的任务。
  4. 处理无更新情况

    • if (currentSchedulerPriority === NoLane):如果 getHighestPriorityLane 返回 NoLane,表示当前根节点上没有任何待处理的更新。在这种情况下,将 root.callbackNoderoot.callbackPriority 重置为 nullNoLane,并直接返回。
    • 目的:当没有更新时,不进行任何调度。
  5. 优先级转换

    • const schedulerPriority = reactPriorityToSchedulerPriority(currentSchedulerPriority):React 内部使用 Lane 模型来管理优先级,而 Scheduler 模块有自己的一套优先级系统(例如 ImmediatePriority, UserBlockingPriority, NormalPriority, LowPriority, IdlePriority)。这个函数负责将 React 的 Lane 优先级映射到 Scheduler 对应的优先级。
    • 目的:将 React 内部的优先级概念转换为 Scheduler 可以理解和处理的优先级。
  6. 计算过期时间 (expirationTime)

    • const expirationTime = calculateExpirationTime(currentSchedulerPriority):对于某些优先级(特别是同步或用户阻塞的优先级),React 会计算一个"过期时间"。这意味着即使浏览器主线程一直很忙,Scheduler 也必须在这个时间点之前强制执行这个任务。这保证了高优先级任务的响应性。
    • 目的:为调度任务设置一个截止时间,确保高优先级任务不会无限期延迟。
  7. 安排新的调度任务

    • root.callbackNode = scheduleCallback(...):这是与 Scheduler 模块交互的关键步骤。scheduleCallback 函数会向 Scheduler 注册一个回调函数(通常是 performWorkOnRoot),并传入计算出的 schedulerPriorityexpirationTime
      • performWorkOnRoot.bind(null, root):这个回调函数是真正启动 React 协调阶段的入口。当 Scheduler 认为时机合适时(例如,浏览器有空闲时间,或者任务过期),它会调用这个函数,并传入 root 对象。
      • { timeout: expirationTime }:将过期时间传递给 Scheduler,以便它可以在必要时强制执行任务。
    • root.callbackPriority = currentSchedulerPriority:更新 root.callbackPriority,记录当前安排的调度任务的优先级。
    • 目的:将 React 的更新任务提交给底层的调度器,由调度器在合适的时机执行,从而启动 Render Phase。

scheduleUpdateOnFiber 通过 ensureRootIsScheduled 将工作单元"注册"到调度系统中,然后调度器在认为合适的时机"唤醒"这个工作单元,从而启动渲染的准备和执行流程。而这马上到第二个阶段。

全流程

为了更直观地理解从更新触发到任务调度的完整链路,我们可以通过以下流程图来展示:

graph TD subgraph "A.更新触发与创建" direction TB A1_Trigger["用户操作/API 调用(setState, render, etc.)"] A2_CreateUpdate["创建Update对象计算 Lane (requestUpdateLane)"] A3_Enqueue["将 Update入队 (enqueueUpdate)"] A4_ScheduleEntry["scheduleUpdateOnFiber(标记Root的pendingLanes)"] A5_EnsureScheduled["ensureRootIsScheduled(判断同步/并发模式)"] end subgraph "B. 任务调度(React Scheduler)" direction TB B1_ScheduleCallback["unstable_scheduleCallback(接收 Reconciler 的任务)"] B2_CreateTask["创建新的Task对象(包含优先级、过期时间、回调函数)"] B3_PushToQueue["将Task推入任务队列(taskQueue 或 timerQueue)"] B4_RequestHostCallback["请求Host调度(requestHostCallback -> MessageChannel)"] end subgraph "C. 事件循环与执行(Browser/Host)" direction TB C1_EventLoop["Event Loop(事件循环)"] C2_MacroTask["执行宏任务(MessageChannel的回调)"] C3_WorkLoop["Scheduler的workLoop(执行到期的Task)"] end %% 连接调用链路 A1_Trigger --> A2_CreateUpdate A2_CreateUpdate --> A3_Enqueue A3_Enqueue --> A4_ScheduleEntry A4_ScheduleEntry --> A5_EnsureScheduled A5_EnsureScheduled --> B1_ScheduleCallback B1_ScheduleCallback --> B2_CreateTask B2_CreateTask --> B3_PushToQueue B3_PushToQueue --> B4_RequestHostCallback B4_RequestHostCallback --> C1_EventLoop C1_EventLoop --> C2_MacroTask C2_MacroTask --> C3_WorkLoop C3_WorkLoop -.-> A1_Trigger;

从流程图中我们可以看到:

  • 职责分离Reconciler 负责 "什么需要更新" (创建 Update、管理 Fiber),而 Scheduler 负责 "何时执行更新"(管理任务队列、与浏览器协作)。
  • 关键桥梁ensureRootIsScheduledunstable_scheduleCallback 是连接这两个模块的核心接口。
  • 异步实现 :通过 MessageChannel 将调度任务推入宏任务队列,Scheduler 实现了不阻塞主线程的异步执行,这是 React 并发模式的基石。
相关推荐
慧一居士几秒前
Axios 和Express 区别对比
前端
I'mxx9 分钟前
【html常见页面布局】
前端·css·html
万少15 分钟前
云测试提前定位和解决问题 萤火故事屋 上架流程
前端·harmonyos·客户端
快起来别睡了24 分钟前
让你的React 路由不再迷路
react.js
brzhang1 小时前
OpenAI 7周发布Codex,我们的数据库迁移为何要花一年?
前端·后端·架构
军军君011 小时前
基于Springboot+UniApp+Ai实现模拟面试小工具三:后端项目基础框架搭建上
前端·vue.js·spring boot·面试·elementui·微信小程序·uni-app
布丁05231 小时前
DOM编程实例(不重要,可忽略)
前端·javascript·html
bigyoung1 小时前
babel 自定义plugin中,如何判断一个ast中是否是jsx文件
前端·javascript·babel
指尖的记忆2 小时前
当代前端人的 “生存技能树”:从切图仔到全栈侠的魔幻升级
前端·程序员
草履虫建模2 小时前
Ajax原理、用法与经典代码实例
java·前端·javascript·ajax·intellij-idea