useEffect vs useLayoutEffect
到底该用哪个?
先说结论:99% 的情况用 useEffect,只在需要测量 DOM 时用 useLayoutEffect。
执行时机不同
useEffect(异步)
- 执行时机:浏览器完成绘制之后
- 特点:不阻塞用户看到界面更新
- 适合:数据请求、埋点、事件订阅
useLayoutEffect(同步)
- 执行时机:React 更新了 DOM,但浏览器还没绘制之前
- 特点:会阻塞浏览器绘制
- 适合:防止视觉闪烁
什么时候必须用 useLayoutEffect?
场景:需要测量 DOM 尺寸,然后根据尺寸设置另一个元素的位置。
用 useEffect 的问题
useEffect(() => {
// 浏览器先画了一次(位置错) -> 执行 effect -> 改位置 -> 浏览器重画(位置对)
// 用户会看到闪烁!
const height = divRef.current.offsetHeight;
otherDiv.style.marginTop = height + 'px';
});
用 useLayoutEffect 的问题
useLayoutEffect(() => {
// React 改 DOM -> 测量并改位置 -> 浏览器绘制
// 这一系列是同步的,用户直接看到最终结果,没有闪烁
const height = divRef.current.offsetHeight;
otherDiv.style.marginTop = height + 'px';
});
SSR 注意
服务端渲染时:
- 两个 Hook 都不会执行(正常)
useLayoutEffect 会报警告(因为暗示有 DOM 操作,但服务端没有)
用条件判断规避:
useLayoutEffect(() => {
if (typeof window !== 'undefined') {
// DOM 操作
}
}, []);
总结
| Hook | 时机 | 阻塞 | 用处 |
|---|
| useEffect | 绘制后 | 不阻塞 | 99% 的场景 |
| useLayoutEffect | 绘制前 | 阻塞 | DOM 测量、布局修正 |
记住:能用 useEffect 就用 useEffect,性能更好。