在 React 项目里,useEffect 大家用得贼溜,但一提到 useLayoutEffect,很多人要么没用过,要么根本分不清啥时候该用,结果页面总是莫名“闪一下”,用户体验直接拉垮。
别急,今天就用几个最典型的例子,带你一次整明白:
- 它和
useEffect到底有啥区别? - 它是怎么帮你解决页面闪烁问题的?
- 什么场景必须用它?
useEffect 和 useLayoutEffect 到底区别在哪?
先看最核心的点:
| Hook | 什么时候执行 | 特点 |
|---|---|---|
useEffect | 浏览器完成渲染后执行 | 异步,不阻塞页面渲染 |
useLayoutEffect | DOM 更新后、浏览器绘制前执行 | 同步,阻塞页面渲染,执行完才渲染到屏幕上 |
一句话:
useEffect是“渲染完了,咱们再干点事儿”useLayoutEffect是“DOM 改完了,页面还没画,先干事儿再画出来”
如果副作用跟布局有关(比如测量高度、计算位置),用 useEffect 就会出现一闪而过的布局错误,再被修正过来;useLayoutEffect 就能在页面渲染前把它干好,防止抖动。
场景 1:最简单的高度测量
来看个最简单的示例:
function App() {
const boxRef = useRef();
useEffect(() => {
console.log('useEffect height', boxRef.current.offsetHeight);
}, []);
useLayoutEffect(() => {
console.log('useLayoutEffect height', boxRef.current.offsetHeight);
}, []);
return <div ref={boxRef} style={{ height: 100 }}></div>;
}
输出结果:
useLayoutEffect height 100
useEffect height 100
这里 useLayoutEffect 会比 useEffect 先打印,因为它在 DOM 更新后、浏览器真正渲染前执行。虽然打印的值一样,但它执行得更及时。
场景 2:内容和样式同时修改,防止闪烁
假设有个盒子,第一次渲染时有默认的文字和高度,接着要改成新的文字和高度。如果用 useEffect,你可能会看到先出现旧内容,再闪一下变成新内容 + 新高度,效果很糟糕。
function App() {
const [content, setContent] = useState('原内容...');
const ref = useRef();
useLayoutEffect(() => {
// 用 useLayoutEffect 阻塞渲染,保证渲染时就已经是新状态
setContent('新内容...');
ref.current.style.height = '200px';
}, []);
return (
<div ref={ref} style={{ height: '50px', background: 'skyblue' }}>
{content}
</div>
);
}
这里如果换成 useEffect,就会先渲染老状态,用户肉眼可见“抖一下”。useLayoutEffect 则是先把 DOM 改好,浏览器再渲染,视觉上丝滑得多。
场景 3:动态居中弹窗,经典闪烁坑
还有个常见场景:弹窗居中。需要先知道弹窗实际高度,再算 margin,让它上下居中。
function Modal() {
const ref = useRef();
useLayoutEffect(() => {
const height = ref.current.offsetHeight;
ref.current.style.marginTop = `${(window.innerHeight - height) / 2}px`;
}, []);
return (
<div
ref={ref}
style={{
background: 'red',
position: 'absolute',
height: '200px',
width: '200px'
}}
>
我是弹窗
</div>
);
}
function App() {
return <Modal />;
}
如果这里用 useEffect,弹窗一开始会出现在左上角,然后才跳到居中位置,用户体验很割裂。而 useLayoutEffect 可以在 DOM 有了高度后,立刻算好位置,让它一开始就乖乖居中。
什么时候必须用 useLayoutEffect?
总结一下核心原则:
✅ 用 useEffect:
- 异步副作用(请求数据、埋点、订阅)
- 跟布局无关,不影响用户第一时间看到的页面
✅ 用 useLayoutEffect:
- 需要同步测量 DOM(获取宽高、位置)
- 需要立刻改动布局(计算位置、设置样式)
- 有“闪一下”或者“跳一下”的视觉抖动
一句话:
凡是跟“页面渲染前”有关的布局修改,都要用 useLayoutEffect,否则就是肉眼可见的闪烁!
写在最后
以后再遇到“页面莫名其妙闪一下”,先别急着骂浏览器,想想是不是 useLayoutEffect 用对了。
希望这篇文章能帮你彻底分清这两个 Hook 的区别,写出更丝滑的交互体验。
如果对你有帮助,点个赞吧!