useEffect vs useLayoutEffect:经典但持续被问,新版解读角度
useEffect vs useLayoutEffect:经典但持续被问,新版解读角度
导语
很多前端同学对 useEffect 和 useLayoutEffect 的第一反应是:”一个异步,一个同步。”这个答案在面试里能过,但在生产里经常被误用。本文从 React 源码执行阶段出发,给你一个更可落地的判断标准。
执行时机的本质区别
React 函数组件的渲染可以拆成三个阶段:
- Render 阶段:调用组件函数,计算出新的 React 元素树。
- Commit 阶段:把变更写入真实 DOM。这个阶段又分为:
commitBeforeMutationEffects:在 DOM 变更后、浏览器绘制前同步执行,useLayoutEffect 在这里跑。commitLayoutEffects:在浏览器绘制后异步执行,useEffect 在这里跑。
所以更准确的说法不是”异步/同步”,而是:
useLayoutEffect 阻塞浏览器绘制;useEffect 不阻塞。
一个直观的对比
假设你在 effect 里做一件耗时 100ms 的同步计算:
- 放在 useLayoutEffect:浏览器会卡 100ms 再画出新帧,用户能感知到掉帧。
- 放在 useEffect:浏览器先画出旧帧,再在空闲时跑你的计算,用户不会卡顿,但可能会看到一帧”旧内容”。
什么时候必须用 useLayoutEffect?
只有一种情况你需要它:读取 DOM 布局后同步触发重渲染,且不能接受闪烁。
典型场景:
- 测量一个 DOM 元素的尺寸/位置,然后立即设置状态触发重排。
- 用
scrollTo或focus做程序化滚动/聚焦。 - 第三方库需要你在 DOM 更新后立即读取布局信息。
一个容易踩的坑:useLayoutEffect + SSR
useLayoutEffect 在服务端渲染时会发出警告,因为服务端没有 DOM。如果你需要 SSR 兼容,可以降级为 useEffect:
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
结论:一个可落地的判断标准
- 默认用 useEffect:数据获取、事件监听、清理定时器……绝大多数副作用都归这里。
- 只有当你需要”读 DOM → 立即写 DOM/状态”且不能有闪烁时,才用 useLayoutEffect。
- 如果用了 useLayoutEffect 导致掉帧:检查里面有没有耗时计算,有的话拆到 Web Worker 或降级为 useEffect。
React 19 并没有改变这两个 Hook 的执行语义,但这个判断标准在并发渲染下更重要——因为并发渲染让”浏览器绘制时机”变得更不可预测,不阻塞绘制 becomes a feature, not a bug。
评论区
0 条评论
登录后可评论。












