最近deepSeek大火,我也迫不及待的使用上了,体验确实很赞!
最近在准备面试,之前一直对 React 里面的 useLayoutEffect 和 useEffect 两个 hook 的区别不是很清楚,于是乎想到了deepSeek,我问了它这个问题,没想到它回答的超乎我的想象。我将它的回答做了一些整理分享出来
区别
useLayoutEffect 和 useEffect 是 React 中两个处理副作用的 Hook,它们的核心区别在于 执行时机 和 对渲染流程的影响。
1. 执行时机差异
useEffect | useLayoutEffect | |
|---|---|---|
| 触发时机 | 在浏览器 绘制完成后 异步执行 | 在 DOM 更新后、浏览器 绘制前 同步执行 |
| 阻塞渲染 | 不会阻塞浏览器绘制 | 会阻塞浏览器绘制直到回调完成 |
2. 底层原理对比
React 渲染流程(简化版):
- Render Phase(生成虚拟 DOM)
- Commit Phase(更新真实 DOM)
- Browser Paint(浏览器绘制页面)
- Effect Callbacks(执行 useEffect)
useLayoutEffect的回调在Commit Phase后立即执行(步骤 2 和 3 之间)useEffect的回调在Browser Paint后执行(步骤 3 之后)
3. 使用场景对比
适合 useLayoutEffect 的场景:
- 需要 同步修改 DOM(例如调整元素尺寸/位置)
- 需要 在浏览器绘制前消除视觉闪烁(如快速切换状态导致的布局抖动)
- 需要 读取 DOM 布局信息(如滚动位置、元素尺寸)
适合 useEffect 的场景:
- 数据获取(API 请求)
- 事件监听/取消监听
- 非紧急的副作用操作(如日志记录)
- 大多数常规副作用(React 官方推荐优先使用)
4. 代码示例对比
示例 1:修改 DOM 样式
// 使用 useEffect(可能看到闪烁)
function Example() {
const ref = useRef();
useEffect(() => {
ref.current.style.marginLeft = '100px'; // 浏览器可能已经绘制过一次
}, []);
return <div ref={ref}>内容</div>;
}
// 使用 useLayoutEffect(无闪烁)
function Example() {
const ref = useRef();
useLayoutEffect(() => {
ref.current.style.marginLeft = '100px'; // 在绘制前完成修改
}, []);
return <div ref={ref}>内容</div>;
}
示例 2:自动聚焦输入框
function AutoFocusInput() {
const inputRef = useRef();
useLayoutEffect(() => {
inputRef.current.focus(); // 在绘制前完成聚焦
}, []);
return <input ref={inputRef} />;
}
5. 注意事项
-
SSR 问题:
useLayoutEffect在服务端渲染(SSR)时会导致警告 -
性能优化:
useLayoutEffect的同步特性可能导致性能问题,应谨慎使用 -
执行顺序:同一组件中
useLayoutEffect总是比useEffect先执行
总结表格
| 特性 | useEffect | useLayoutEffect |
|---|---|---|
| 执行阶段 | 异步(不阻塞渲染) | 同步(阻塞渲染) |
| 最佳场景 | 大多数副作用操作 | DOM 测量/同步修改 |
| 服务端渲染兼容性 | ✅ | ⚠️(需要条件判断) |
| React 官方推荐优先级 | 优先使用 | 特殊场景使用 |
当需要操作 DOM 且要求 视觉一致性 时使用 useLayoutEffect,其他情况优先使用 useEffect。