标准答案

  1. useEffect 在提交 DOM 更新后、浏览器绘制之后执行,不会阻塞首帧绘制,适合请求、订阅、日志、事件监听等副作用。
  2. useLayoutEffect 在 DOM 已更新但浏览器绘制前同步执行,可以读取布局并同步 setState 修正位置。
  3. useLayoutEffect 会阻塞浏览器绘制,滥用会影响页面响应和首屏体验。
  4. 需要测量 DOM 尺寸、计算浮层位置、避免视觉闪烁时,可以考虑 useLayoutEffect。
  5. SSR 场景中 useLayoutEffect 在服务端没有真实布局环境,要注意只在客户端执行或使用框架提供的兼容写法。

题目解析

这道题可以用“是否阻塞绘制”区分。大多数业务副作用用 useEffect 即可。

如果用 useEffect 先绘制错误位置,再修正位置,用户可能看到闪烁;这类场景才适合 useLayoutEffect。

useLayoutEffect 不是更高级的 useEffect。它更强也更危险,要用在布局测量和同步视觉修正这类明确场景。

代码示例

下面的 tooltip 需要先读取触发元素位置,再在绘制前设置浮层坐标。

TSX
function Tooltip({ targetRef }: { targetRef: RefObject<HTMLElement> }) {
  const [style, setStyle] = useState({ left: 0, top: 0 })

  useLayoutEffect(() => {
    const rect = targetRef.current?.getBoundingClientRect()
    if (!rect) {
      return
    }

    setStyle({
      left: rect.left,
      top: rect.bottom + 8
    })
  }, [targetRef])

  return <div style={style}>提示内容</div>
}

常见误区

  • 所有副作用都用 useLayoutEffect,导致绘制被不必要地阻塞。
  • 需要避免视觉闪烁的布局测量却用 useEffect,页面先错位再跳到正确位置。
  • 在 SSR 环境中不考虑 useLayoutEffect 的客户端限制。

作者信息