React.memo() 和 useMemo() 的用法是什么,有哪些区别?

React.memo() 和 useMemo() 的用法与区别

React.memo() 和 useMemo() 都是 React 提供的性能优化 API,但它们解决的问题和使用场景完全不同。下面我将从多个维度详细解析它们的用法和区别。

一、React.memo() - 组件级记忆

1. 基本用法

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});

// 或箭头函数
const MyComponent = React.memo((props) => {
  /* 使用 props 渲染 */
});

2. 核心功能

  • 作用对象:包装整个函数组件
  • 优化原理:对组件 props 进行浅比较,如果 props 没有变化,则跳过重新渲染
  • 类比概念:类似于类组件的 PureComponent

3. 适用场景

  • 纯展示型组件(输入相同的 props 总是输出相同结果)
  • 渲染开销较大的组件
  • 频繁重新渲染但 props 很少变化的组件

4. 自定义比较函数

const MyComponent = React.memo(
  (props) => {/* 渲染 */},
  (prevProps, nextProps) => {
    // 返回 true 表示跳过重新渲染
    return prevProps.id === nextProps.id;
  }
);

5. 典型示例

const UserProfile = React.memo(({ user }) => {
  console.log('UserProfile 渲染');
  return <div>{user.name}</div>;
});

function App() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>点击 {count}</button>
      <UserProfile user={{ name: '张三' }} />
    </div>
  );
}
// 点击按钮时 UserProfile 不会重新渲染

二、useMemo() - 值记忆

1. 基本用法

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

2. 核心功能

  • 作用对象:包装计算量大的函数结果
  • 优化原理:依赖项不变时返回缓存值,避免重复计算
  • 类比概念:类似于计算属性(Vue)或记忆化函数

3. 适用场景

  • 昂贵的计算(如复杂数学运算、大数据转换)
  • 避免不必要的重新创建对象/数组
  • 作为其他 Hook 的依赖项优化

4. 典型示例

function App({ list }) {
  const [filter, setFilter] = useState('');
  
  const filteredList = useMemo(() => {
    console.log('重新计算列表');
    return list.filter(item => item.includes(filter));
  }, [list, filter]); // 只有 list 或 filter 变化时才重新计算

  return (
    <div>
      <input value={filter} onChange={(e) => setFilter(e.target.value)} />
      <ul>
        {filteredList.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
}

三、核心区别对比

维度React.memo()useMemo()
作用对象整个函数组件单个值/计算结果
优化目标避免不必要的组件重新渲染避免重复计算昂贵操作
比较方式浅比较 props(可自定义)依赖项数组比较(Object.is)
使用位置组件外部(包装组件)组件内部(在函数体中使用)
返回值记忆化的组件记忆化的值
典型用例纯展示组件、大型列表项复杂计算、对象/数组创建

四、组合使用示例

const ExpensiveComponent = React.memo(({ data }) => {
  const processedData = useMemo(() => {
    return data.map(item => ({
      ...item,
      fullName: `${item.firstName} ${item.lastName}`
    }));
  }, [data]);

  return (
    <ul>
      {processedData.map(item => (
        <li key={item.id}>{item.fullName}</li>
      ))}
    </ul>
  );
});

function App() {
  const [count, setCount] = useState(0);
  const [users, setUsers] = useState([]);
  
  // 模拟数据获取
  useEffect(() => {
    fetchUsers().then(setUsers);
  }, []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>计数: {count}</button>
      <ExpensiveComponent data={users} />
    </div>
  );
}

五、使用注意事项

1. React.memo() 陷阱

  • 不合理的 props 比较:深层对象比较可能适得其反
    // 反模式 - 每次都会重新渲染
    <MemoizedComponent style={{ color: 'red' }} />
    
    // 正确做法 - 提取到常量
    const redStyle = { color: 'red' };
    <MemoizedComponent style={redStyle} />
    
  • 子组件问题:如果子组件本身经常变化,记忆化可能无效
    // 反模式 - onClick 每次都是新函数
    <MemoizedComponent onClick={() => {}} />
    
    // 正确做法 - 使用 useCallback
    const handleClick = useCallback(() => {}, []);
    <MemoizedComponent onClick={handleClick} />
    

2. useMemo() 陷阱

  • 过早优化:简单计算不需要 useMemo
    // 不需要 - 计算太简单
    const total = useMemo(() => a + b, [a, b]);
    
    // 直接计算即可
    const total = a + b;
    
  • 依赖项遗漏:会导致返回过期值
    // 错误 - 遗漏了 b 作为依赖项
    const sum = useMemo(() => a + b, [a]);
    

六、性能优化决策树

避免组件重渲染
避免重复计算
需要优化性能?
优化什么?
使用React.memo
使用useMemo
Props中有非原始值?
配合useMemo/useCallback
直接使用React.memo
计算是否昂贵?
使用useMemo
直接计算

七、总结与最佳实践

  1. 优先考虑设计:良好的组件拆分比滥用记忆化更有效
  2. 测量后优化:使用 React DevTools Profiler 确认性能瓶颈
  3. 合理使用记忆化
    • React.memo 用于静态或低频变化的组件
    • useMemo 用于昂贵计算或稳定引用
    • useCallback 配合 React.memo 使用(处理函数 props)
  4. 避免过度优化:记忆化本身有开销,简单场景可能适得其反

记住:性能优化应该是基于实际测量结果的有针对性的措施,而不是开发初期的默认实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员流年大运

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值