Scheduler 和 Reconciliation 有什么区别?
Scheduler 管“什么时候做、先做哪个”,Reconciliation 管“要做什么改动”
先记住一句话:Scheduler 负责调度更新的优先级和执行时机;Reconciliation 负责在 Render 阶段比较新旧树,计算出需要提交的变化。
它们经常一起出现,但不是同一层概念。Scheduler 更接近任务调度系统,Reconciliation 更接近 UI diff 和 Fiber 构建过程。
1. 一次更新里的位置
一次 React 更新可以简化成:
更短地说:
2. Scheduler 做什么?
Scheduler 关注的是“任务怎么排”。它会配合 React 的优先级模型,决定不同更新的执行顺序和让步时机。
典型职责包括:
- 给不同更新安排优先级;
- 让紧急更新优先执行,例如输入、点击;
- 让非紧急更新可以延后,例如
startTransition中的大列表渲染; - 在并发渲染中判断是否需要让出主线程;
- 当更高优先级更新到来时,打断或重做低优先级 Render。
例如搜索场景中:
输入框的 setInput 是紧急更新,用户应该马上看到输入结果;startTransition 里的 setQuery 可以稍后完成。Scheduler 会帮助 React 优先处理更紧急的工作,避免列表渲染把输入响应卡住。
需要注意:Scheduler 不负责比较 JSX,也不负责判断哪个 DOM 节点要插入或删除。它只是帮助 React 决定什么时候开始、继续、暂停或切换 Render 工作。
3. Reconciliation 做什么?
Reconciliation 关注的是“UI 变成什么样”。它发生在 Render 阶段,主要工作是根据新的渲染结果和旧 Fiber 树进行比较,生成新的 workInProgress Fiber 树。
典型职责包括:
- 执行函数组件,得到新的 React Element;
- 根据
type和key判断 Fiber 是否可以复用; - 构建或复用 workInProgress Fiber 节点;
- 比较 props、children 和 Hook 状态;
- 标记需要在 Commit 阶段执行的变化,例如插入、更新、删除。
例如:
当 items 顺序、内容或数量变化时,Reconciliation 会根据 key 判断哪些 li 可以复用,哪些需要移动、新增或删除。它算出的是“变化计划”,真正修改 DOM 仍然发生在 Commit 阶段。
4. 和 Fiber 的关系
Fiber 是 Reconciliation 的工作单元,也是 Scheduler 能够调度 Render 的基础。
可以这样理解:
Fiber 节点上保存了 child、sibling、return 等指针,React 可以手动遍历 Fiber 树,而不是完全依赖 JavaScript 调用栈递归到底。这使 Render 阶段可以暂停,之后从上次记录的位置继续。
所以 Fiber 让 Reconciliation 可以被拆成一个个工作单元;Scheduler 则利用这种可拆分能力,让 React 在合适的时候处理或暂停这些工作。
5. 和 Render / Commit 的关系
React 更新常被分成两个主要阶段:
Scheduler 主要影响 Render 阶段,因为 Render 阶段还没有真正修改 DOM,中间结果可以被丢弃。Commit 阶段一旦开始,就需要尽快连续完成,避免页面处于不一致状态。
Reconciliation 本身也属于 Render 阶段。它可以被 Scheduler 打断,但它不负责把变化提交到页面。
6. 为什么 Render 可以被打断?
因为 Render 阶段做的是计算:
在 workInProgress 完成并进入 Commit 之前,用户看到的仍然是旧的 current 树对应的 UI。未提交的 workInProgress 只是内存中的计算结果,因此可以被暂停、重做或放弃。
这也是为什么组件 render 必须保持纯粹:如果在 render 阶段发送请求、改 DOM 或写全局变量,React 一旦中断或重做 Render,就可能造成重复执行或状态不一致。
7. 高优先级任务来了怎么办?
更高优先级任务到来时,Scheduler 不是把当前 Reconciliation “强行结束”,而是让 React 的 Render 工作循环在 Fiber 工作单元之间停下来。当前还没提交的 workInProgress 可以之后继续,也可以因为过时而被重做或丢弃。
一个简化的工作循环可以理解为:
其中 performUnitOfWork 会处理一个 Fiber:
shouldYield() 可以理解成 Scheduler 给 React 的让步信号:时间片快用完了,或者有更紧急的工作要先处理。因为 React 每次处理的是一个个 Fiber 工作单元,所以它可以在单元之间暂停,而不是必须一次递归到底。
例如低优先级列表更新正在 Render:
暂停后有几种可能:
- 继续:如果只是时间片到了,没有冲突的更高优先级更新,React 之后可以从记录的
nextUnitOfWork继续。 - 切换:如果高优先级更新到来,React 会优先处理更紧急的 lane。
- 重做或丢弃:如果低优先级 workInProgress 已经过时,React 可以基于最新 current Fiber 重新计算;未提交的结果不会影响页面。
暂停后之所以能继续,是因为 Fiber 把递归渲染拆成了可保存进度的工作单元。React 不需要依赖 JavaScript 调用栈记住遍历位置,而是把进度保存在内存中的变量和 Fiber 树上:
当 shouldYield() 让 Render 暂停时,React 会保留当前的 workInProgress 和 nextUnitOfWork。之后 Scheduler 再次安排这项工作时,如果这份 workInProgress 仍然有效,就可以从 nextUnitOfWork 继续执行:
如果暂停期间来了会影响结果的新更新,或者当前低优先级任务已经过时,React 就不一定恢复原来的 workInProgress,而可能重新从 current Fiber 开始计算。也就是说,暂停后可以恢复,但恢复不是承诺;React 会优先保证最终提交的是和当前更新队列一致的结果。
这里的关键是:Render 阶段还没有修改 DOM,页面仍然对应旧的 current Fiber。因此中断 Reconciliation 不会让用户看到半成品 UI。只有某次 workInProgress 完整完成并进入 Commit 后,它才会真正影响页面。
8. 两者的区别总结
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 重新计算。

