← 返回目录

第 16 卷

第 16 / 28 卷日期:

13return list.filter(item => item.toString().includes(search)).sort((a, b)

=> a - b);

14}, [list, search]); // 依赖项:仅 list 或 search 变化时重新计算 15

16return ( 17<div>

18<input 19type="text"

20value={search}

21onChange={(e) => setSearch(e.target.value)}
22placeholder="搜索..."
23/>
24<ul>
25{filteredList.map(item => <li key={item}>{item}</li>)}
26</ul>
27</div>
28);
29 }

说明:未使用 useMemo 时,每次输入框变化(search 变化)或父组件渲染,都会重新执行过滤排序;使用 useMemo 后,仅当 list 或 search 变化时才执行一次计算,后续渲染直接复用缓存结果,减

少性能开销。

5. 注意事项

• 不要过度使用:useCallback 和 useMemo 本身也有性能开销(需要存储缓存、对比依赖项),对

于简单函数/计算,直接创建/执行的成本更低,无需缓存。

• 依赖数组必须完整:若缓存的函数/计算依赖组件内的变量(如 state、props),必须将这些变量

加入依赖数组,否则可能导致使用过期的值(闭包陷阱)。

• useMemo 不可用于副作用:useMemo 的回调函数必须是纯函数(无 DOM 操作、网络请求等副作

用),因为 React 可能会根据性能需求,在不渲染组件的情况下执行该函数。

二十七、useEffect 与 useLayoutEffect 在 React 中的不同及其使用场景

1. 核心区别:执行时机

useEffect 和 useLayoutEffect 均是 React Hooks 中用于处理「副作用」的工具(如 DOM 操作、网络请求、定时器、事件监听等),核心差异在于执行时机不同,这直接决定了它们的适用场景和性能影

• useEffect:「异步执行」,在浏览器完成页面渲染(绘制 DOM)之后执行。React 会先更新

• useLayoutEffect:「同步执行」,在 React 完成 DOM 更新但尚未将 DOM 绘制到页面之前执

行。React 会先执行 useLayoutEffect 的回调函数,待回调完成后,再将更新后的 DOM 绘制到页面,会阻塞浏览器的渲染流程。

补充:两者的语法完全一致,均接收两个参数(副作用回调函数、依赖数组),但执行时机的差异导致了使用场景的严格区分。

2. 执行流程对比

(1)useEffect 的执行流程

  1. 组件触发重新渲染(如状态变化、props 变化);
  2. React 计算并更新虚拟 DOM,生成真实 DOM 操作指令;
  3. 浏览器执行 DOM 操作,将更新后的页面绘制到屏幕上;
  4. 异步执行 useEffect 的回调函数(若依赖数组满足条件);
  5. 回调函数执行完成(若有清理函数,会在下次回调执行前执行)。

(2)useLayoutEffect 的执行流程

  1. 组件触发重新渲染;
  2. React 计算并更新虚拟 DOM,生成真实 DOM 操作指令;
  3. 浏览器执行 DOM 操作(更新 DOM 树,但未绘制到屏幕);
  4. 同步执行 useLayoutEffect 的回调函数(若依赖数组满足条件);
  5. 回调函数执行完成(若有清理函数,优先执行);
  6. 浏览器将更新后的 DOM 绘制到屏幕上。

3. 关键区别总结

对比维度useEffectuseLayoutEffect
执行时机浏览器渲染(绘制)之后,异浏览器渲染(绘制)之前,同
步执行步执行
是否阻塞渲染不阻塞,不会影响页面加载速阻塞,回调执行时间过长会导
度致页面卡顿
DOM 操作影响若在回调中修改 DOM,可能在绘制前修改 DOM,可避免

执行顺序同一组件中,晚于同一组件中,早于 useEffect useLayoutEffect 执行执行

4. 使用场景详解

(1)useEffect 的使用场景

useEffect 是处理副作用的「默认选择」,适用于不需要阻塞渲染、不依赖 DOM 布局信息的场景,常见案例:

  • 网络请求(如组件挂载时获取数据);
  • 定时器/间隔器(如组件挂载时启动定时器,卸载时清除);
  • 事件监听(如监听窗口大小变化、滚动事件);
  • 数据持久化(如将状态同步到 localStorage);
  • 非紧急的 DOM 操作(如添加动画效果,允许轻微延迟)。

示例:组件挂载时获取用户数据

Code block

1 2function UserProfile() {

3const [user, setUser] = useState(null);

4const [loading, setLoading] = useState(true);

5 6useEffect(() => {

7// 网络请求(异步,不阻塞渲染)

8const fetchUser = async () => { 9try {

10const res = await fetch('https://api.example.com/user');
11const data = await res.json();
12setUser(data);
13} catch (err) {
14console.error('获取用户失败', err);
15} finally {
16setLoading(false);
17}
18};
全站分卷索引 · 28 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷
  5. 5第 5 卷
  6. 6第 6 卷
  7. 7第 7 卷
  8. 8第 8 卷
  9. 9第 9 卷
  10. 10第 10 卷
  11. 11第 11 卷
  12. 12第 12 卷
  13. 13第 13 卷
  14. 14第 14 卷
  15. 15第 15 卷
  16. 16第 16 卷
  17. 17第 17 卷
  18. 18第 18 卷
  19. 19第 19 卷
  20. 20第 20 卷
  21. 21第 21 卷
  22. 22第 22 卷
  23. 23第 23 卷
  24. 24第 24 卷
  25. 25第 25 卷
  26. 26第 26 卷
  27. 27第 27 卷
  28. 28第 28 卷