React useMemo(当依赖项未变化,重复渲染时直接返回上一次缓存计算结果,而非重新执行计算)

文章目录

  • [React `useMemo` 介绍:提升组件性能的关键利器](#React useMemo 介绍:提升组件性能的关键利器)
  • [3. 在 Next.js App Router 中使用 useMemo(示例)](#3. 在 Next.js App Router 中使用 useMemo(示例))
    • [下面示例基于 **Next.js 13+ App Router**,展示如何在页面组件中使用 `useMemo` 来处理 expensive computation。](#下面示例基于 Next.js 13+ App Router,展示如何在页面组件中使用 useMemo 来处理 expensive computation。)
      • [📁 项目结构](#📁 项目结构)
      • [📄 `app/page.tsx`](#📄 app/page.tsx)
      • [🔍 代码解析](#🔍 代码解析)
    • 下面是去掉useMemo的版本,我们来看看是什么情况
  • [4. 使用 useMemo 的注意事项](#4. 使用 useMemo 的注意事项)
  • [5. 总结](#5. 总结)

React useMemo 介绍:提升组件性能的关键利器

在 React 开发中,组件渲染性能是一个永恒的话题。当组件包含复杂计算、依赖重的派生数据,或在高频渲染场景中运行时,就会带来性能下降。这时,React 提供的 useMemo 就能发挥重要作用------用于缓存计算结果,避免不必要的重复运算,从而提升应用性能。

1. useMemo 是什么?

useMemo 是 React 的一个 Hook,用于缓存某个计算过程的结果(memoized value)。其核心思想是:

当依赖项未变化时,重复渲染时直接返回上一次的计算结果,而不是重新执行计算。

基本语法

ts 复制代码
const memoizedValue = useMemo(() => {
  return expensiveComputation(a, b);
}, [a, b]);
  • 第一个参数是返回计算结果的函数。
  • 第二个参数是依赖项数组,当依赖项变化时会重新执行计算。

2. 为什么需要 useMemo

场景一:昂贵计算

例如需要处理大量数据、复杂筛选或排序逻辑,如果每次渲染都触发,会导致卡顿。

场景二:避免子组件不必要渲染

当 memoized 值作为 props 传递给子组件时,如果不使用 useMemo,每次渲染都会生成新引用,从而导致子组件重新渲染。

场景三:稳定引用(如避免 useEffect 重复触发)


3. 在 Next.js App Router 中使用 useMemo(示例)

下面示例基于 Next.js 13+ App Router ,展示如何在页面组件中使用 useMemo 来处理 expensive computation。

📁 项目结构

复制代码
app/
  page.tsx

📄 app/page.tsx

ts 复制代码
"use client";

import { useMemo, useState } from "react";

export default function HomePage() {
  const [count, setCount] = useState(0);
  const [query, setQuery] = useState("");

  // 模拟昂贵计算(比如过滤大型数据列表)
  const filteredList = useMemo(() => {
    console.log("执行 expensive computation...");
    const data = Array.from({ length: 50000 }, (_, i) => `Item ${i}`);
    return data.filter((item) => item.includes(query));
  }, [query]);

  return (
    <main style={{ padding: 20 }}>
      <h1>React useMemo 示例(Next.js App Router)</h1>

      <div style={{ marginTop: 20 }}>
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="输入查询关键字(触发 expensive computation)"
          style={{ padding: 8, width: 300 }}
        />
      </div>

      <div style={{ marginTop: 20 }}>
        <button
          onClick={() => setCount(count + 1)}
          style={{ padding: "6px 12px" }}
        >
          点击计数:{count}
        </button>
      </div>

      <div style={{ marginTop: 20 }}>
        <h3>过滤结果({filteredList.length} items)</h3>
        <ul>
          {filteredList.slice(0, 20).map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
        <small>(仅显示前 20 条)</small>
      </div>
    </main>
  );
}

在输入框输入会触发console.log("执行 expensive computation..."); 打印,点击计数不会,这样就避免了不必要的大规模计算

🔍 代码解析

  • filteredList 依赖 query,当 query 改变时才会重新执行过滤。
  • 点击"计数"按钮只修改 count,不触发 expensive computation。
  • 控制台可以观察到 expensive computation... 打印次数明显减少。

下面是去掉useMemo的版本,我们来看看是什么情况

ts 复制代码
// app/page.tsx (Next.js 13 app-router, client component)

"use client";

import { useState } from "react"; // 移除了 useMemo 导入

export default function HomePage() {
  const [count, setCount] = useState(0);
  const [query, setQuery] = useState("");

  // 直接计算而不使用 useMemo(每次渲染都会重新执行)
  console.log("执行 expensive computation..."); // 注意:这条日志会在每次渲染时触发
  const data = Array.from({ length: 50000 }, (_, i) => `Item ${i}`);
  const filteredList = data.filter((item) => item.includes(query));

  return (
    <main style={{ padding: 20 }}>
      <h1>React useMemo 示例(Next.js App Router)</h1>

      <div style={{ marginTop: 20 }}>
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="输入查询关键字(触发 expensive computation)"
          style={{ padding: 8, width: 300 }}
        />
      </div>

      <div style={{ marginTop: 20 }}>
        <button
          onClick={() => setCount(count + 1)}
          style={{ padding: "6px 12px" }}
        >
          点击计数:{count}
        </button>
      </div>

      <div style={{ marginTop: 20 }}>
        <h3>过滤结果({filteredList.length} items)</h3>
        <ul>
          {filteredList.slice(0, 20).map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
        <small>(仅显示前 20 条)</small>
      </div>
    </main>
  );
}

发现点击计数也会触发console.log("执行 expensive computation..."); 打印,相当于下面代码也被执行,非常耗资源:

ts 复制代码
const data = Array.from({ length: 50000 }, (_, i) => `Item ${i}`);
const filteredList = data.filter((item) => item.includes(query));

4. 使用 useMemo 的注意事项

❗不要过度使用 useMemo

过度缓存会带来额外内存和计算开销,如果计算量很小,反而得不偿失。

❗依赖项一定要正确填写

依赖项写错会导致:

  • 缓存不更新(bug)
  • 每次都重新执行(性能浪费)

✔ 最佳实践

  • 用于昂贵计算(排序、过滤、格式化大型数据)
  • 用于稳定引用(如传给 memoized 子组件)
  • 用于避免 useEffect 不必要触发

5. 总结

useMemo 是 React 提供的重要性能优化工具,适用于缓存昂贵计算或保持引用稳定。在 Next.js App Router 中,它和普通的 React 组件用法完全一致,但要注意不要滥用。

在真实项目中,合理应用 useMemo 可以显著提高渲染性能,让页面更加流畅。

相关推荐
爱喝白开水a几秒前
前端AI自动化测试:brower-use调研让大模型帮你做网页交互与测试
前端·人工智能·大模型·prompt·交互·agent·rag
董世昌411 分钟前
深度解析ES6 Set与Map:相同点、核心差异及实战选型
前端·javascript·es6
吃杠碰小鸡1 小时前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农2 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
forestsea2 小时前
深入理解Redisson RLocalCachedMap:本地缓存过期策略全解析
redis·缓存·redisson
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js