在 React 中,useEffect 和 useLayoutEffect 的核心区别在于 它们在浏览器渲染流程中的触发时机。
简单来说:useEffect 是异步 执行的,不会阻塞屏幕绘制;而 useLayoutEffect 是同步执行的,会在屏幕绘制之前完成。
1. 执行时机对比
- useEffect (最常用)
- 状态更新,组件重新渲染。
- 浏览器将组件的变更绘制到屏幕上。
- 绘制完成后 ,
useEffect的回调函数异步执行。
-
优点: 不会阻塞用户界面,性能更好。
-
useLayoutEffect (特殊用途)
- 状态更新,组件重新渲染。
- React 计算出 DOM 的变化。
- 在浏览器绘制屏幕之前 ,
useLayoutEffect同步执行。 - 浏览器将最终结果绘制到屏幕。
- 优点: 可以防止用户看到视觉上的"闪烁"或抖动。
2. 核心区别摘要
| 特性 | useEffect | useLayoutEffect |
|---|---|---|
| 执行时机 | 屏幕绘制后 (After paint) | 屏幕绘制前 (Before paint) |
| 执行方式 | 异步 (Asynchronous) | 同步 (Synchronous) |
| 性能影响 | 低,不阻塞渲染 | 高,可能会阻塞页面显示 |
| 典型场景 | 数据获取、订阅、日志记录 | 读取布局、测量 DOM、手动修改 DOM |
3. 什么时候用 useLayoutEffect?
你几乎应该总是首选 useEffect。只有当你遇到以下情况时才考虑 useLayoutEffect:
场景:避免界面闪烁
假设你需要根据一个按钮的位置来定位一个弹窗。
- 如果用
useEffect:浏览器先画出位置不对的弹窗,然后useEffect运行并修正位置,用户会看到弹窗"跳"了一下。 - 如果用
useLayoutEffect:React 会在浏览器画出弹窗前就把位置算好,用户直接看到位置正确的弹窗。
4. 代码演示
javascript
import React, { useState, useLayoutEffect, useEffect, useRef } from 'react';
function ComparisonExample() {
const [value, setValue] = useState(0);
const divRef = useRef();
useLayoutEffect(() => {
// 这里的代码会在屏幕更新前执行
if (value === 0) {
// 假设我们要强制把 0 变成一个随机大数
setValue(10 + Math.random() * 200);
}
}, [value]);
return (
<div ref={divRef} onClick={() => setValue(0)}>
Value is: {value} (点击我重置为0)
</div>
);
}
注意: 在服务器端渲染 (SSR) 时,
useLayoutEffect会引发警告,因为它要求同步访问浏览器 API。这种情况下通常建议使用useEffect或进行环境判断。
你想针对某个具体的业务场景(比如动画或复杂的 DOM 测量)看看该如何选择这两个 Hook 吗?