useEffect vs useLayoutEffect:经典但持续被问,新版解读角度

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 元素的尺寸/位置,然后立即设置状态触发重排。
  • scrollTofocus 做程序化滚动/聚焦。
  • 第三方库需要你在 DOM 更新后立即读取布局信息。

一个容易踩的坑:useLayoutEffect + SSR

useLayoutEffect 在服务端渲染时会发出警告,因为服务端没有 DOM。如果你需要 SSR 兼容,可以降级为 useEffect:

const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;

结论:一个可落地的判断标准

  1. 默认用 useEffect:数据获取、事件监听、清理定时器……绝大多数副作用都归这里。
  2. 只有当你需要”读 DOM → 立即写 DOM/状态”且不能有闪烁时,才用 useLayoutEffect
  3. 如果用了 useLayoutEffect 导致掉帧:检查里面有没有耗时计算,有的话拆到 Web Worker 或降级为 useEffect。

React 19 并没有改变这两个 Hook 的执行语义,但这个判断标准在并发渲染下更重要——因为并发渲染让”浏览器绘制时机”变得更不可预测,不阻塞绘制 becomes a feature, not a bug

评论区

0 条评论

登录后可评论。

查看完整榜单
查看完整榜单
查看完整榜单