React Fiber架构里render和commit阶段到底怎么分工的?
最近在看React源码,对Fiber架构有点懵。我知道更新分render和commit两个阶段,但具体哪些工作是在render阶段做、哪些留到commit阶段执行?比如useEffect和useLayoutEffect的回调是在哪个阶段调度的?
我试过在组件里加一些console.log,发现render阶段会多次执行(比如被高优先级任务打断后重来),但commit好像只走一次。那像DOM操作这种副作用,是不是必须等到commit阶段才安全?有没有什么原则可以判断某段逻辑该放在哪个阶段处理?
到了commit阶段,那就动真格了。这个阶段必须同步执行,一口气跑完,绝不能断。这时候才会把render阶段算出来的结果应用到真实DOM上。你问的DOM操作,绝对不能在render阶段做,不然一来一回重算,页面就乱套了。
关于那两个Hook,位置很关键。useLayoutEffect是在commit阶段执行,具体点说是在DOM变更之后、浏览器绘制之前,它是同步的,所以这里面改DOM会阻塞页面渲染。useEffect则不一样,它是在commit阶段彻底结束后,浏览器把画面画出来了,再异步调度执行的。
如果你要判断逻辑放哪,记住一个死理:render阶段必须是纯函数,别有副作用,别碰DOM,别搞定时器。凡是涉及DOM读写、网络请求、订阅事件,统统扔到commit阶段(也就是useLayoutEffect或者useEffect)里去。
给你写个伪代码感受一下这个流程:
搞WordPress这么多年,看惯了WP的钩子机制,其实React Fiber这套也是类似的调度思想,只是更底层更激进。理解了“计算”和“执行”的边界,这玩意儿就不难懂了。