字
字节笔记本
2026年2月17日
useEffect 和 useLayoutEffect 的区别详解
API中转
¥120
本文深入讲解 React 中 useEffect 和 useLayoutEffect 两个 Hook 的区别,通过实际代码示例帮助开发者理解何时使用哪个 Hook,避免常见的闪屏问题。
useEffect 和 useLayoutEffect 的区别
在 React 函数组件中,useEffect 和 useLayoutEffect 都是用于处理副作用的 Hook,但它们在执行时机上有重要区别。理解这个区别对于编写高性能、无视觉问题的 React 应用至关重要。
useEffect
基本特性
useEffect 是 React 中最常用的副作用 Hook,在 90% 的场景下都应该使用它。
执行时机:
- 在 render 结束后执行
- 不会阻塞浏览器绘制(paint)
- 采用异步方式执行
与 Class 组件的对比:
- useEffect 是异步的,在浏览器绘制后才执行
- componentDidMount 和 componentDidUpdate 是同步的,在 render 结束后立即执行
- useEffect 在大部分场景下比 Class 方式性能更好
适用场景
- 数据获取(API 调用)
- 订阅/取消订阅
- 手动修改 DOM(不影响布局的情况)
- 日志记录
基本用法
jsx
import React, { useEffect } from 'react';
function Example() {
useEffect(() => {
// 副作用逻辑
console.log('组件挂载或更新');
return () => {
// 清理函数
console.log('组件卸载');
};
}, [/* 依赖数组 */]);
return <div>Example</div>;
}useLayoutEffect
基本特性
useLayoutEffect 与 useEffect 的签名完全相同,但执行时机不同。
执行时机:
- 在 DOM 更新完成后立即执行
- 在浏览器进行任何绘制之前运行完成
- 同步执行,阻塞浏览器绘制
适用场景
- 需要测量 DOM 节点布局
- 修改 DOM 并改变页面样式
- 需要避免视觉闪烁(闪屏)的情况
- 同步执行必要的 DOM 操作
为什么要阻塞绘制?
当 useEffect 中的操作需要处理 DOM 并且会改变页面样式时,如果不用 useLayoutEffect,可能会出现闪屏问题。这是因为:
- 浏览器先绘制了初始状态
- useEffect 异步执行,修改了 DOM
- 用户看到内容闪烁或跳动
useLayoutEffect 在绘制前同步执行,确保用户看到的是最终状态。
实际对比示例
使用 useEffect 的问题
jsx
import React, { useEffect, useRef } from "react";
import TweenMax from "gsap/TweenMax";
const Animate = () => {
const REl = useRef(null);
useEffect(() => {
// 将方块移动到 600px 位置
TweenMax.to(REl.current, 0, { x: 600 });
}, []);
return (
<div className='animate'>
<div ref={REl} className="square">
square
</div>
</div>
);
};现象: 可以清楚看到有一个一闪而过的方块(闪屏)
原因:
- 组件渲染,方块在初始位置(0px)
- 浏览器绘制,用户看到方块
- useEffect 异步执行,方块移动到 600px
- 用户看到方块从 0px 跳到 600px
使用 useLayoutEffect 解决
jsx
import React, { useLayoutEffect, useRef } from "react";
import TweenMax from "gsap/TweenMax";
const Animate = () => {
const REl = useRef(null);
useLayoutEffect(() => {
// 将方块移动到 600px 位置
TweenMax.to(REl.current, 0, { x: 600 });
}, []);
return (
<div className='animate'>
<div ref={REl} className="square">
square
</div>
</div>
);
};现象: 每次刷新,页面基本没变化,看不到闪屏
原因:
- 组件渲染
- useLayoutEffect 同步执行,方块直接设置在 600px 位置
- 浏览器绘制,用户直接看到最终状态
执行时机对比图
text
组件渲染流程:
1. Render 阶段
↓
2. DOM 更新
↓
3. useLayoutEffect 执行(同步,阻塞绘制)
↓
4. 浏览器绘制(Paint)
↓
5. useEffect 执行(异步,不阻塞)选择指南
| 场景 | 推荐 Hook | 原因 |
|---|---|---|
| 数据获取 | useEffect | 不阻塞渲染,性能更好 |
| 事件订阅 | useEffect | 不需要同步执行 |
| 修改 DOM 样式 | useLayoutEffect | 避免闪屏 |
| 测量 DOM 尺寸 | useLayoutEffect | 需要在绘制前获取准确值 |
| 动画初始位置 | useLayoutEffect | 避免初始位置闪烁 |
| 日志记录 | useEffect | 不需要阻塞 |
注意事项
1. 服务端渲染(SSR)
useLayoutEffect 会在服务端渲染时触发警告,因为服务端没有 DOM。如果组件需要在服务端渲染,可以使用以下方式:
jsx
import React, { useEffect, useLayoutEffect } from 'react';
const useIsomorphicLayoutEffect = typeof window !== 'undefined'
? useLayoutEffect
: useEffect;2. 性能影响
由于 useLayoutEffect 阻塞浏览器绘制,过度使用会影响应用性能。仅在必要时使用。
3. 默认值
如果不确定用哪个,始终优先使用 useEffect。只有当出现视觉问题时,才考虑切换到 useLayoutEffect。
总结
- useEffect:异步执行,不阻塞绘制,适用于大多数场景
- useLayoutEffect:同步执行,阻塞绘制,适用于需要避免闪屏的 DOM 操作
记住这个简单的原则:先尝试 useEffect,如果遇到闪屏问题,再换成 useLayoutEffect。
原文作者: LastStranger
来源: 简书
阅读数: 45,726
分享: