Web Dev

详解React 性能优化之cache 的核心作用

2025年1月17日 (5个月前)

在 React/Next.js 中,cache 是 React 18+ 提供的一个实验性 API,用于 跨组件渲染周期缓存数据,避免重复计算或请求。


1. cache 的核心作用

  • 缓存函数结果:相同输入下直接返回缓存值,跳过重复执行。
  • 跨组件共享缓存:多个组件调用同一函数时复用缓存。
  • 自动垃圾回收:缓存会随组件卸载自动清理。

2. 基本用法

(1) 缓存普通函数

import { cache } from "react";

// 定义缓存函数
const getData = cache(async (id) => {
  const res = await fetch(`https://api.example.com/data/${id}`);
  return res.json();
});

// 组件中使用
async function ComponentA({ id }) {
  const data = await getData(id); // 首次调用会请求,后续复用缓存
}

async function ComponentB({ id }) {
  const data = await getData(id); // 直接读取缓存
}

(2) 缓存 React 组件

const CachedComponent = cache(function MyComponent({ data }) {
  return <div>{data}</div>;
});

function Parent() {
  return (
    <>
      <CachedComponent data="A" /> {/* 首次渲染 */}
      <CachedComponent data="A" /> {/* 跳过渲染,复用缓存 */}
    </>
  );
}

3. Next.js 中的关键应用场景

(1) 数据请求去重

// utils/fetch.js
export const fetchUser = cache(async (id) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
});

// 多个地方使用同一数据
async function Profile() {
  const user = await fetchUser(1); // 实际只发一次请求
}

async function Dashboard() {
  const sameUser = await fetchUser(1); // 读取缓存
}

(2) 优化服务端组件

// app/page.js
import { cache } from "react";
import db from "lib/db";

const getPosts = cache(() => db.posts.findMany());

export default async function Page() {
  const posts = await getPosts(); // 同请求只执行一次
  return <PostList posts={posts} />;
}

4. 缓存行为控制

(1) 手动清除缓存

import { unstable_cache } from "next/cache";

const getData = unstable_cache(
  async () => fetchData(),
  ["data-key"], // 缓存键
  { revalidate: 3600 } // 1小时后失效
);

(2) 动态缓存键

const getUser = cache(async (id, role) => {
  // 根据参数自动生成缓存键
});

const user1 = await getUser(1, "admin"); // 独立缓存
const user2 = await getUser(1, "user"); // 不同缓存

5. 与 Next.js 缓存机制对比

| 特性 | react/cache | Next.js unstable_cache | | -------------- | -------------------- | ------------------------ | | 作用范围 | 单次渲染周期 | 跨请求持久化 | | 适用场景 | 组件间共享数据 | 全应用级数据缓存 | | 失效控制 | 组件卸载自动清理 | 需手动设置 revalidate | | 数据新鲜度 | 每次渲染可能重新执行 | 可配置过期时间 |


6. 性能优化示例

缓存昂贵计算

const calculateStats = cache((data) => {
  // 假设是复杂计算
  return expensiveCalculation(data);
});

function Chart({ data }) {
  const stats = calculateStats(data); // 相同data跳过计算
  return <svg>{/* ... */}</svg>;
}

避免重复 API 调用

// 多个组件需要相同数据
const fetchGlobalConfig = cache(async () => {
  return fetch("/api/config");
});

async function Header() {
  const config = await fetchGlobalConfig();
}

async function Footer() {
  const sameConfig = await fetchGlobalConfig(); // 命中缓存
}

7. 注意事项

  1. 仅服务端使用cache 主要针对 Server Components,客户端组件需用 useMemo
  2. 不可变参数:缓存依赖参数需可序列化(如字符串/数字,避免对象/函数)。
  3. 调试技巧
    console.log(cache); // 查看缓存实例(开发环境)
    

总结

  • 何时用:跨组件共享数据、避免重复计算/请求时。
  • 最佳实践
    • 对纯函数或数据获取函数使用 cache
    • 结合 Next.js 的 unstable_cache 实现长期缓存
    • 避免缓存高频变化的动态数据

通过合理使用 cache,可显著提升 React/Next.js 应用的渲染效率! 🚀