React 组件的渲染机制默认是“牵一发而动全身”:父组件更新,所有子组件都会跟随重渲染。当组件树变得复杂时,这会造成巨大的性能浪费。
通过 React.memo、useMemo 和 useCallback 这三个工具,你可以精准控制组件的更新时机,切断不必要的渲染链条。
1. 使用 React.memo 阻止组件重渲染
React.memo 是一个高阶组件,用于包裹整个组件。它的作用是:对比 前后的 Props,如果 Props 没变,就跳过 该组件的渲染,直接复用上一次的结果。
适用场景:纯函数组件、渲染开销较大、Props 经常保持不变的子组件。
打开 子组件文件。
引入 React。
使用 React.memo() 包裹 组件导出。
import React from 'react';
// 普通组件定义
const MyComponent = ({ name }) => {
console.log('子组件渲染了');
return <div>{name}</div>;
};
// 使用 memo 包裹后导出
export default React.memo(MyComponent);
验证 效果。当父组件更新但 name 属性未变化时,控制台不会打印“子组件渲染了”。
2. 理解引用相等性问题
仅仅使用 React.memo 往往不够。React 组件在每次渲染时,内部声明的对象 {} 和函数 () => {} 都会生成全新的内存地址。
如果你将一个“新生成的函数”或“新生成的对象”作为 Props 传给被 memo 包裹的子组件,React.memo 会认为 Props 发生了变化(因为内存地址变了),从而触发重渲染。
要解决这个问题,必须配合 useCallback 或 useMemo 来稳定引用地址。
3. 使用 useCallback 缓存函数
useCallback 用于缓存函数实例。它确保在依赖项不变的情况下,函数在多次渲染中保持相同的内存地址。
适用场景:将函数作为 Props 传递给子组件、函数作为其他 Hook 的依赖项。
找到 父组件中传递给子组件的函数。
引入 useCallback。
包裹 函数定义。
声明 依赖项数组。
import React, { useCallback } from 'react';
const ParentComponent = () => {
// 每次渲染都返回同一个函数引用,除非 count 变化
const handleClick = useCallback(() => {
console.log('点击事件触发');
}, []); // 空数组表示该函数永远不变
return <ChildComponent onClick={handleClick} />;
};
核心语法:
const cachedFn = useCallback(fn, dependencies);
4. 使用 useMemo 缓存计算结果
useMemo 用于缓存计算结果(返回值)。它可以避免在每次渲染时重复执行昂贵的计算,也可以用于缓存对象以保持引用相等。
适用场景:复杂的数学计算、数据转换、需要保持引用稳定的对象 Props。
识别 组件中开销较大的计算逻辑或对象构造。
引入 useMemo。
包裹 计算逻辑。
声明 依赖项数组。
import React, { useMemo } from 'react';
const ParentComponent = ({ items }) => {
// 只有 items 变化时才重新计算,否则直接返回缓存结果
const sortedList = useMemo(() => {
console.log('正在进行昂贵的排序...');
return items.sort((a, b) => a - b);
}, [items]);
// 缓存对象,防止传递给子组件时因引用变化导致重渲染
const styleObj = useMemo(() => ({ color: 'red' }), []);
return <ChildComponent list={sortedList} style={styleObj} />;
};
核心语法:
const cachedValue = useMemo(calculateValue, dependencies);
5. 区分三者使用场景
这三个 API 经常被混淆,以下是快速决策指南。
| API | 缓存内容 | 核心目的 | 典型应用场景 |
|---|---|---|---|
memo |
组件 (JSX) | 跳过组件渲染 | 子组件 Props 变化少,子组件渲染开销大 |
useCallback |
函数 | 保持函数引用稳定 | 传递函数给子组件;函数作为 useEffect 依赖 |
useMemo |
任意值 | 避免重复计算/保持对象引用 | 复杂数据计算;传递对象/数组给子组件 |
6. 实战优化流程
在实际开发中,按照以下步骤操作可避免过度优化或优化无效。
定位 性能瓶颈。使用 React DevTools 的 Profiler 面板查看 哪些组件频繁重渲染。
检查 子组件。如果子组件重渲染但 Props 未变,添加 React.memo。
检查 Props 来源。如果 memo 失效,检查 父组件传递的是否为引用类型(函数、对象、数组)。
修复 引用问题。
- 若为函数,使用
useCallback包裹。 - 若为对象或计算结果,使用
useMemo包裹。
验证 优化结果。再次运行 Profiler,确认 无关的状态更新不再触发子组件渲染。

暂无评论,快来抢沙发吧!