React Fiber架构里render和commit阶段到底怎么分工的?

Air-兴敏 阅读 52

最近在看React源码,对Fiber架构有点懵。我知道更新分render和commit两个阶段,但具体哪些工作是在render阶段做、哪些留到commit阶段执行?比如useEffectuseLayoutEffect的回调是在哪个阶段调度的?

我试过在组件里加一些console.log,发现render阶段会多次执行(比如被高优先级任务打断后重来),但commit好像只走一次。那像DOM操作这种副作用,是不是必须等到commit阶段才安全?有没有什么原则可以判断某段逻辑该放在哪个阶段处理?

我来解答 赞 11 收藏
二维码
手机扫码查看
1 条解答
设计师秀玲
React Fiber这套机制,说白了就是把“计算”和“执行”彻底分开。render阶段其实就是在内存里瞎折腾,构建Fiber树,算算哪些节点变了,也就是常说的Reconciliation。因为只是纯计算,不涉及真实DOM,所以它是可中断的。浏览器忙不过来或者有更高优先级任务插队,React随时可以把这阶段的工作扔了,回头重算一遍,这就是你看到console.log多次打印的原因。

到了commit阶段,那就动真格了。这个阶段必须同步执行,一口气跑完,绝不能断。这时候才会把render阶段算出来的结果应用到真实DOM上。你问的DOM操作,绝对不能在render阶段做,不然一来一回重算,页面就乱套了。

关于那两个Hook,位置很关键。useLayoutEffect是在commit阶段执行,具体点说是在DOM变更之后、浏览器绘制之前,它是同步的,所以这里面改DOM会阻塞页面渲染。useEffect则不一样,它是在commit阶段彻底结束后,浏览器把画面画出来了,再异步调度执行的。

如果你要判断逻辑放哪,记住一个死理:render阶段必须是纯函数,别有副作用,别碰DOM,别搞定时器。凡是涉及DOM读写、网络请求、订阅事件,统统扔到commit阶段(也就是useLayoutEffect或者useEffect)里去。

给你写个伪代码感受一下这个流程:

// Render阶段 (可中断)
function workLoop() {
while (nextUnitOfWork && !shouldYield()) {
// 这里只是创建Fiber节点,标记副作用,不操作DOM
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}

// Commit阶段 (不可中断)
function commitRoot(root) {
const finishedWork = root.finishedWork;

// 1. beforeMutation:比如getSnapshotBeforeUpdate
// 2. mutation:执行DOM节点插入、更新、删除
commitMutationEffects(finishedWork);

// 3. layout:执行useLayoutEffect回调,DOM已经变了但浏览器还没画
commitLayoutEffects(finishedWork);

// 4. 调度useEffect:等浏览器画完再执行
schedulePassiveEffects(finishedWork);
}


搞WordPress这么多年,看惯了WP的钩子机制,其实React Fiber这套也是类似的调度思想,只是更底层更激进。理解了“计算”和“执行”的边界,这玩意儿就不难懂了。
点赞 1
2026-03-04 00:05