Scheduler 和 Reconciliation 有什么区别?

Scheduler 管“什么时候做、先做哪个”,Reconciliation 管“要做什么改动”

先记住一句话:Scheduler 负责调度更新的优先级和执行时机;Reconciliation 负责在 Render 阶段比较新旧树,计算出需要提交的变化。

它们经常一起出现,但不是同一层概念。Scheduler 更接近任务调度系统,Reconciliation 更接近 UI diff 和 Fiber 构建过程。

1. 一次更新里的位置

一次 React 更新可以简化成:

setState / props 变化 / context 变化

创建 update,并标记更新优先级

Scheduler 安排这次更新什么时候执行

Render 阶段执行 Reconciliation

生成 workInProgress Fiber 树和副作用标记

Commit 阶段把变化提交到 DOM

更短地说:

Scheduler
决定哪项更新先进入 Render,以及 Render 能不能暂停、继续或被更高优先级工作打断

Reconciliation
在 Render 阶段根据新 props/state/context 计算下一棵 Fiber 树和变化标记

2. Scheduler 做什么?

Scheduler 关注的是“任务怎么排”。它会配合 React 的优先级模型,决定不同更新的执行顺序和让步时机。

典型职责包括:

  • 给不同更新安排优先级;
  • 让紧急更新优先执行,例如输入、点击;
  • 让非紧急更新可以延后,例如 startTransition 中的大列表渲染;
  • 在并发渲染中判断是否需要让出主线程;
  • 当更高优先级更新到来时,打断或重做低优先级 Render。

例如搜索场景中:

function SearchPage({ allItems }) {
  const [input, setInput] = useState("");
  const [query, setQuery] = useState("");

  function handleChange(event) {
    const value = event.target.value;

    setInput(value);

    startTransition(() => {
      setQuery(value);
    });
  }

  return (
    <>
      <input value={input} onChange={handleChange} />
      <ResultList query={query} items={allItems} />
    </>
  );
}

输入框的 setInput 是紧急更新,用户应该马上看到输入结果;startTransition 里的 setQuery 可以稍后完成。Scheduler 会帮助 React 优先处理更紧急的工作,避免列表渲染把输入响应卡住。

需要注意:Scheduler 不负责比较 JSX,也不负责判断哪个 DOM 节点要插入或删除。它只是帮助 React 决定什么时候开始、继续、暂停或切换 Render 工作。

3. Reconciliation 做什么?

Reconciliation 关注的是“UI 变成什么样”。它发生在 Render 阶段,主要工作是根据新的渲染结果和旧 Fiber 树进行比较,生成新的 workInProgress Fiber 树。

典型职责包括:

  • 执行函数组件,得到新的 React Element;
  • 根据 typekey 判断 Fiber 是否可以复用;
  • 构建或复用 workInProgress Fiber 节点;
  • 比较 props、children 和 Hook 状态;
  • 标记需要在 Commit 阶段执行的变化,例如插入、更新、删除。

例如:

function List({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

items 顺序、内容或数量变化时,Reconciliation 会根据 key 判断哪些 li 可以复用,哪些需要移动、新增或删除。它算出的是“变化计划”,真正修改 DOM 仍然发生在 Commit 阶段。

4. 和 Fiber 的关系

Fiber 是 Reconciliation 的工作单元,也是 Scheduler 能够调度 Render 的基础。

可以这样理解:

Fiber
React 内部表示组件树和工作进度的数据结构

Reconciliation
处理 Fiber,构建下一棵 workInProgress Fiber 树

Scheduler
安排 React 什么时候处理这些 Fiber 工作单元

Fiber 节点上保存了 childsiblingreturn 等指针,React 可以手动遍历 Fiber 树,而不是完全依赖 JavaScript 调用栈递归到底。这使 Render 阶段可以暂停,之后从上次记录的位置继续。

所以 Fiber 让 Reconciliation 可以被拆成一个个工作单元;Scheduler 则利用这种可拆分能力,让 React 在合适的时候处理或暂停这些工作。

5. 和 Render / Commit 的关系

React 更新常被分成两个主要阶段:

Render 阶段
可中断、可暂停、可重做
执行 Reconciliation
计算下一棵 Fiber 树
收集变化标记



Commit 阶段
不可中断
把变化真正应用到 DOM
执行 ref、layout effect
调度 passive effect

Scheduler 主要影响 Render 阶段,因为 Render 阶段还没有真正修改 DOM,中间结果可以被丢弃。Commit 阶段一旦开始,就需要尽快连续完成,避免页面处于不一致状态。

Reconciliation 本身也属于 Render 阶段。它可以被 Scheduler 打断,但它不负责把变化提交到页面。

6. 为什么 Render 可以被打断?

因为 Render 阶段做的是计算:

旧 current Fiber 树

根据新更新计算

新的 workInProgress Fiber 树

在 workInProgress 完成并进入 Commit 之前,用户看到的仍然是旧的 current 树对应的 UI。未提交的 workInProgress 只是内存中的计算结果,因此可以被暂停、重做或放弃。

这也是为什么组件 render 必须保持纯粹:如果在 render 阶段发送请求、改 DOM 或写全局变量,React 一旦中断或重做 Render,就可能造成重复执行或状态不一致。

7. 高优先级任务来了怎么办?

更高优先级任务到来时,Scheduler 不是把当前 Reconciliation “强行结束”,而是让 React 的 Render 工作循环在 Fiber 工作单元之间停下来。当前还没提交的 workInProgress 可以之后继续,也可以因为过时而被重做或丢弃。

一个简化的工作循环可以理解为:

while (nextUnitOfWork !== null && !shouldYield()) {
  nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}

其中 performUnitOfWork 会处理一个 Fiber:

performUnitOfWork(fiber)

beginWork:处理当前 Fiber,计算或协调子 Fiber

如果有 child,返回 child 作为下一个工作单元

如果没有 child,进入 completeWork

寻找 sibling;没有 sibling 就回到 return 父节点

shouldYield() 可以理解成 Scheduler 给 React 的让步信号:时间片快用完了,或者有更紧急的工作要先处理。因为 React 每次处理的是一个个 Fiber 工作单元,所以它可以在单元之间暂停,而不是必须一次递归到底。

例如低优先级列表更新正在 Render:

低优先级更新开始

React 构建 workInProgress Fiber

处理一部分 Fiber 后检查 shouldYield()

发现用户输入等高优先级更新

暂停当前 Render

优先处理高优先级更新

暂停后有几种可能:

  1. 继续:如果只是时间片到了,没有冲突的更高优先级更新,React 之后可以从记录的 nextUnitOfWork 继续。
  2. 切换:如果高优先级更新到来,React 会优先处理更紧急的 lane。
  3. 重做或丢弃:如果低优先级 workInProgress 已经过时,React 可以基于最新 current Fiber 重新计算;未提交的结果不会影响页面。

暂停后之所以能继续,是因为 Fiber 把递归渲染拆成了可保存进度的工作单元。React 不需要依赖 JavaScript 调用栈记住遍历位置,而是把进度保存在内存中的变量和 Fiber 树上:

nextUnitOfWork
下一次要继续处理的 Fiber

workInProgress Fiber
已经构建出的部分 Fiber 树和相关 flags

current Fiber
页面当前正在显示的已提交树

shouldYield() 让 Render 暂停时,React 会保留当前的 workInProgressnextUnitOfWork。之后 Scheduler 再次安排这项工作时,如果这份 workInProgress 仍然有效,就可以从 nextUnitOfWork 继续执行:

暂停 Render

主线程处理浏览器绘制、用户输入或更高优先级任务

Scheduler 再次调度 React 工作

如果旧 workInProgress 仍可用

从 nextUnitOfWork 继续 performUnitOfWork

如果暂停期间来了会影响结果的新更新,或者当前低优先级任务已经过时,React 就不一定恢复原来的 workInProgress,而可能重新从 current Fiber 开始计算。也就是说,暂停后可以恢复,但恢复不是承诺;React 会优先保证最终提交的是和当前更新队列一致的结果。

这里的关键是:Render 阶段还没有修改 DOM,页面仍然对应旧的 current Fiber。因此中断 Reconciliation 不会让用户看到半成品 UI。只有某次 workInProgress 完整完成并进入 Commit 后,它才会真正影响页面。

8. 两者的区别总结

对比项SchedulerReconciliation
关注点更新的优先级和执行时机新旧 UI 的比较和变化计算
所属层面调度层渲染计算层
主要阶段主要影响 Render 阶段发生在 Render 阶段
是否直接 diff JSX
是否直接改 DOM
是否可被打断它负责让 Render 可让步Reconciliation 工作可被打断
典型关键词priority、lanes、time slicing、transitionFiber、diff、key、flags、workInProgress

9. 面试怎么回答?

可以这样回答:

Scheduler 负责调度 React 更新,决定不同优先级的更新什么时候执行,Render 工作是否需要暂停、继续或被更高优先级任务打断。Reconciliation 发生在 Render 阶段,负责根据新的 props、state 和 context 重新计算 React Element,并和旧 Fiber 树比较,生成新的 workInProgress Fiber 树以及插入、更新、删除等变化标记。Scheduler 管执行时机,Reconciliation 管变化计算;两者都不直接修改 DOM,真正修改 DOM 发生在 Commit 阶段。

如果继续追问“它们和 Fiber 是什么关系”,可以补一句:

Fiber 是 React 表示组件树和保存工作进度的数据结构。Reconciliation 以 Fiber 为单位计算下一棵树,Scheduler 则利用 Fiber 可拆分、可恢复的特性调度 Render 工作。

如果追问“高优先级任务如何结束当前 Reconciliation”,可以这样答:

更准确地说不是结束,而是暂停当前 Render work loop。React 在处理 Fiber 单元之间检查是否应该让出主线程;如果有更高优先级更新,就先处理更紧急的 lane。由于当前 workInProgress 还没有 commit,它可以之后继续,也可以基于最新 current Fiber 重做或丢弃,不会让页面进入半更新状态。

如果追问“暂停后怎么恢复”,可以这样答:

React 能恢复暂停的 Render,是因为 Fiber 把渲染拆成了可保存进度的工作单元。暂停时,React 保留当前 workInProgress Fiber 树和 nextUnitOfWork;Scheduler 之后重新调度时,如果这份工作仍然有效,就从 nextUnitOfWork 继续执行。如果期间来了更高优先级更新或让结果过时的更新,React 也可能放弃原来的 workInProgress,基于 current Fiber 重新计算。