news 2026/5/8 21:09:18

React 性能优化:memo、useMemo 与 useCallback 用法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
React 性能优化:memo、useMemo 与 useCallback 用法

你在 React 项目开发中,是不是遇到过组件无意义重复渲染、复杂计算耗时过长的问题?比如父组件仅仅修改自身状态,子组件却跟着重新渲染;每次渲染都执行复杂的数组过滤、排序,导致页面卡顿。React 提供的 memo、useMemo、useCallback 三个优化工具,能精准解决这些性能痛点,让组件渲染更高效。本文结合实战代码,带你吃透这三个工具的核心用法,轻松实现 React 项目性能优化。

一、核心认知:三个工具的优化定位

在使用之前,先明确三者的优化场景,避免用错地方反而增加代码复杂度:

  1. memo:用于优化函数组件的重复渲染,针对「组件本身无变化却因父组件渲染而被迫渲染」的场景,相当于类组件的 PureComponent。
  2. useMemo:用于缓存复杂计算的结果,避免每次组件渲染都重复执行耗时计算,返回的是「计算结果」。
  3. useCallback:用于缓存函数的引用,避免每次组件渲染都创建新的函数实例,返回的是「函数本身」。

三者的核心共性:通过缓存减少不必要的计算和渲染,提升组件执行效率。

二、实战一:memo 优化组件重复渲染

memo 是一个高阶组件,接收一个函数组件作为参数,返回一个优化后的组件。它会浅比较组件的 props,只有当 props 发生变化时,组件才会重新渲染。

1. 未优化的问题示例

父组件修改自身状态,子组件无 props 变化却跟着重复渲染:

import React, { useState } from 'react'; // 子组件:无 props 依赖,却会随父组件渲染而重复渲染 function ChildComponent() { console.log('子组件:无意义重复渲染了'); return <div style={{ marginTop: '20px' }}>我是子组件,无 props 传入</div>; } // 父组件 function ParentComponent() { const [count, setCount] = useState(0); return ( <div style={{ padding: '20px', border: '1px solid #eee', borderRadius: '8px' }}> <h3>父组件计数器:{count}</h3> <button onClick={() => setCount(count + 1)}>点击+1</button> <ChildComponent /> </div> ); } export default ParentComponent;

2. 使用 memo 优化后的示例

import React, { useState, memo } from 'react'; // 优化:用 memo 包裹子组件,避免无意义重复渲染 const ChildComponent = memo(() => { console.log('子组件:只有 props 变化才会渲染'); return <div style={{ marginTop: '20px' }}>我是子组件,无 props 传入</div>; }); // 父组件(代码不变) function ParentComponent() { const [count, setCount] = useState(0); return ( <div style={{ padding: '20px', border: '1px solid #eee', borderRadius: '8px' }}> <h3>父组件计数器:{count}</h3> <button onClick={() => setCount(count + 1)}>点击+1</button> <ChildComponent /> </div> ); } export default ParentComponent;

3. memo 核心说明

  • memo 仅做「浅比较」:如果 props 是对象/数组类型,仅比较引用地址,不深入比较内部属性;
  • 若需深比较,可传递第二个参数(自定义比较函数),但不推荐(深比较本身会消耗性能);
  • memo 仅优化子组件因父组件渲染导致的无意义渲染,若组件自身状态变化,仍会正常渲染。

三、实战二:useCallback 缓存函数引用

当父组件向子组件传递函数 props 时,每次父组件渲染都会创建新的函数实例,导致子组件的 props 引用变化,即使使用 memo 也无法阻止子组件重复渲染。此时需要 useCallback 缓存函数引用。

优化示例

import React, { useState, memo, useCallback } from 'react'; // 子组件:接收函数 props const ChildComponent = memo(({ onHandleClick }) => { console.log('子组件:函数 props 引用未变化,不渲染'); return <button onClick={onHandleClick} style={{ marginTop: '10px' }}>子组件按钮</button>; }); // 父组件 function ParentComponent() { const [count, setCount] = useState(0); // 优化:用 useCallback 缓存函数,仅当依赖变化时才创建新函数 const handleChildClick = useCallback(() => { console.log('子组件按钮被点击'); }, []); // 空依赖:函数引用永久缓存,除非组件卸载 return ( <div style={{ padding: '20px', border: '1px solid #eee', borderRadius: '8px' }}> <h3>父组件计数器:{count}</h3> <button onClick={() => setCount(count + 1)}>点击+1</button> <ChildComponent onHandleClick={handleChildClick} /> </div> ); } export default ParentComponent;

useCallback 核心说明

  • useCallback 接收两个参数:需要缓存的函数、依赖数组;
  • 只有当依赖数组中的变量发生变化时,才会创建新的函数实例,否则返回缓存的函数引用;
  • 通常与 memo 配合使用,单独使用 useCallback 无优化意义。

四、实战三:useMemo 缓存复杂计算结果

当组件中存在复杂计算(如大数据数组排序、过滤、数学运算)时,每次组件渲染都会重复执行这些计算,消耗大量性能。useMemo 可以缓存计算结果,仅当依赖变化时才重新计算。

优化示例

import React, { useState, useMemo } from 'react'; // 模拟复杂计算:大数据数组过滤与排序 function complexCalculation(list) { console.log('执行复杂计算...'); return list.filter(item => item > 50).sort((a, b) => b - a); } function CalculationDemo() { const [count, setCount] = useState(0); // 模拟大数据数组 const numberList = [12, 67, 89, 34, 90, 56, 78, 23]; // 优化:用 useMemo 缓存计算结果,仅当 numberList 变化时才重新计算 const resultList = useMemo(() => { return complexCalculation(numberList); }, [numberList]); // 依赖:仅 numberList 变化时重新计算 return ( <div style={{ padding: '20px', border: '1px solid #eee', borderRadius: '8px' }}> <h3>父组件计数器:{count}</h3> <button onClick={() => setCount(count + 1)}>点击+1</button> <div style={{ marginTop: '20px' }}> <h4>复杂计算结果(大于50的数降序排列):</h4> <p>{resultList.join(', ')}</p> </div> </div> ); } export default CalculationDemo;

useMemo 核心说明

  • useMemo 接收两个参数:计算函数、依赖数组;
  • 计算函数的返回值即为缓存结果,组件渲染时若依赖未变化,直接返回缓存结果,不执行计算函数;
  • 避免用 useMemo 缓存简单计算(如 count + 1),缓存本身也有开销,反而得不偿失;
  • useMemo 还能用于避免传递给子组件的对象/数组 props 引用频繁变化(配合 memo 使用)。

五、避坑指南与最佳实践

  1. 避免过度优化:只有当组件出现明显性能问题(如卡顿、重复渲染频繁)时,才使用这三个工具,不要一上来就无脑优化,增加代码复杂度。
  2. 依赖数组不要遗漏:useCallback 和 useMemo 的依赖数组必须包含所有在内部使用的组件变量,否则会获取到旧值,导致逻辑异常。
  3. memo 不解决深比较问题:若子组件 props 是复杂对象,不要依赖 memo 阻止渲染,优先简化 props,或使用不可变数据(如 Immer)。
  4. useMemo 与 useCallback 区分使用:需要缓存「计算结果」用 useMemo,需要缓存「函数引用」用 useCallback,不要混淆。
  5. 避免在 useMemo/useCallback 中执行副作用:副作用逻辑应放在 useEffect 中,这两个工具仅用于缓存,执行副作用会导致不可预期的问题。

六、总结

React 中 memo、useMemo、useCallback 的核心价值在于「减少不必要的渲染和计算,提升组件性能」,核心要点可概括为:

  1. memo:优化函数组件重复渲染,浅比较 props,仅当 props 变化时才渲染。
  2. useCallback:缓存函数引用,配合 memo 避免子组件因函数 props 引用变化而重复渲染。
  3. useMemo:缓存复杂计算结果,仅当依赖变化时才重新计算,提升渲染效率。
  4. 避坑关键:不过度优化、不遗漏依赖、区分使用场景、不执行副作用。
  5. 核心原则:性能优化以「解决实际问题」为前提,优先保证代码可读性,再进行针对性优化。

掌握这三个工具的用法,你可以轻松解决 React 项目中的常见性能问题,让组件渲染更高效,为用户带来更流畅的使用体验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 7:24:18

基于OpenPLC的产线控制实战案例详解

用树莓派OpenPLC重构产线控制&#xff1a;一个工业自动化工程师的实战手记最近接手了一个老产线升级项目&#xff0c;客户原用的是三菱FX3U PLC&#xff0c;配了个触摸屏&#xff0c;运行了快八年。系统稳定但扩展性极差——想加两个传感器&#xff1f;得换PLC模块、改接线、重…

作者头像 李华
网站建设 2026/5/3 8:47:36

从巨额亏损中提炼出的3条颠覆性交易心法

引言&#xff1a;你是否也在用“猜谜”的方式炒股&#xff1f;你是否也曾在股市的海洋中感到迷茫&#xff1f;每天被海量的信息淹没&#xff0c;反复追涨杀跌&#xff0c;最终却发现账户数字不增反减。我们总想找到那个能够精准预测市场的“水晶球”&#xff0c;但现实往往是&a…

作者头像 李华
网站建设 2026/5/5 6:49:30

电影解说详细教程:从「一条视频」到「持续更新」

很多人第一次做电影解说&#xff0c;都会经历一个相似的过程&#xff1a;第一条视频做得很认真&#xff0c;从选片到剪辑反复打磨&#xff0c;虽然播放量未必高&#xff0c;但至少“做出来了”。可问题也往往从这里开始——第二条、第三条迟迟没动静&#xff0c;更新开始断断续…

作者头像 李华
网站建设 2026/5/2 18:51:57

springboot151基于javaweb的线上鲜花商城管理系统的设计与实现

目录具体实现截图摘要系统所用技术介绍写作提纲源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 摘要 随着互联网技术的快速发展&#xff0c;电子商务已成为现代商业活动的重要组成部分。鲜花作为一种特殊的商品&#xff…

作者头像 李华
网站建设 2026/5/5 1:08:48

AI系统的可解释性与透明度提升方法

AI系统的可解释性与透明度提升方法关键词&#xff1a;AI系统、可解释性、透明度、提升方法、模型解释、决策过程摘要&#xff1a;本文聚焦于AI系统的可解释性与透明度提升方法。随着AI技术在众多领域的广泛应用&#xff0c;其决策过程的不透明性引发了诸多问题&#xff0c;如信…

作者头像 李华