标准答案
- useEffect 在提交 DOM 更新后、浏览器绘制之后执行,不会阻塞首帧绘制,适合请求、订阅、日志、事件监听等副作用。
- useLayoutEffect 在 DOM 已更新但浏览器绘制前同步执行,可以读取布局并同步 setState 修正位置。
- useLayoutEffect 会阻塞浏览器绘制,滥用会影响页面响应和首屏体验。
- 需要测量 DOM 尺寸、计算浮层位置、避免视觉闪烁时,可以考虑 useLayoutEffect。
- 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 的客户端限制。