缓存
概述
概述
| Mechanism | What | Where | Purpose | Duration |
|---|---|---|---|---|
| Request Memoization | Return values of functions | Server | Re-use data in a React Component tree | Per-request lifecycle |
| Data Cache | Data | Server | Store data across user requests and deployments | Persistent (can be revalidated) |
| Full Route Cache | HTML and RSC payload | Server | Reduce rendering cost and improve performance | Persistent (can be revalidated) |
| Router Cache | RSC Payload | Client | Reduce server requests on navigation | User session or time-based |

动态渲染和静态渲染
- 静态路由默认被缓存;
- 动态路由在请求时渲染, 不会进行 Router Cache 以外的缓存;

工作流程
服务器渲染
- React 将 RSC 转换成 RSC Payload;
- next 将 RSC Payload 和对应 js 转换为 html;
Full Route Cache
- next 在服务器缓存对应路由的 RSC Payload 和其渲染结果 HTML;
react hydration
- 立刻接受预先渲染的无交互逻辑的 HTML, 仅限于首屏加载;
- 接受 RSC payload, 构建组件树, 并更新 DOM;
- next 水合 client component 和 js 代码, 使其具有交互性;
Router Cache
- 客户端缓存 RSC Payload;
Subsequent Navigation
- next 检查对应路由是否具有 Router Cache;
- 若已缓存, 跳过请求返回 RSC Payload;
- 若未缓存, 向服务器发送请求, 并将请求结果缓存至 Route Cache;
Request Memoization
memoize
- react 行为;
- react 拓展 fetch API, 自动缓存具有相同 URL 和 option 的请求;
- 多个组件同时调用相同请求只需请求一次;
async function getItem() {
// The `fetch` function is automatically memoized and the result
// is cached
const res = await fetch("https://.../item/1");
return res.json();
}
// This function is called twice, but only executed the first time
const item = await getItem(); // cache MISS
// The second call could be anywhere in your route
const item = await getItem(); // cache HIT
工作机制
- Request Memoization 作用于同一次渲染流程;
- 客户端第一次发送请求, 服务器执行函数并缓存数据至内存;
- 客户端再次发送请求, 直接从缓存中获取数据;
- 该次渲染完毕, 清空内存中的缓存数据;

应用范围
- 只作用于 fetch 的 GET 请求;
- 只作用于 React Component tree;
生命周期
- 单次渲染流程;
- 可通过选择退出清空缓存;
- 使用 AbortController;
const { signal } = new AbortController();
fetch(url, { signal });
Data Cache
Data Cache
- next 行为;
- next 拓展 fetch API, 缓存请求结果数据;
- 任何类型请求均被缓存;
工作机制
- 客户端发送请求, next 健在对应数据是否缓存;
- 若未缓存, 服务期执行代码返回数据, 持久化缓存至 Data Cache, 并对其 memoize;
- 若已缓存, 直接返回数据, 并对其 memoize;
- 对于 next config 中设置
{ cache: 'no-store' }的数据;- 服务期执行代码返回数据, 并对其 memoize;

生命周期
- 持久化存储;
- 可通过重新验证或者选择退出清空缓存;
选择退出
- 设置
cache: 'no-store'选项;
// Opt out of caching for an individual `fetch` request
fetch(`https://...`, { cache: "no-store" });
Full Route Cache
Full Route Cache
- next 在构建时自动缓存 route 请求结果;
- next 在服务器缓存对应路由的 RSC Payload 和其渲染结果 HTML;
生命周期
- 持久化存储;
- 可通过清空 Data Cache, 使用动态函数, 重新部署清空缓存;
Route Cache
Route Cache
- next 行为;
- 将 RSC Payload 缓存至客户端内存中;
- 提高路由导航性能;
工作机制
- next 检查对应路由是否具有 Router Cache;
- 若已缓存, 跳过请求, 直接返回缓存 RSC Payload;
- 若未缓存, 向服务器发送请求, 并将请求对应 RSC Payload 缓存至 Route Cache;

生命周期
- 用户会话期间;
- 固定时间间隔自动清空;
- 动态渲染: 30s;
- 静态渲染: 5min;
- 可通过重新验证, 使用动态函数清空缓存;
prefetch
- 使用
prefetch={true}或 router.prefetch; - 强制缓存 5min, 无论静态路由还是动态路由;
其他
react 原生数据缓存
- 使用 react 的 cache api;
- 实验性行为, 只能应用于 ssr, 以后才学;
// util.ts
import { cache } from "react";
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id });
return item;
});
// page.ts
import { getItem } from "@/utils/get-item";
export const revalidate = 3600; // revalidate the data at most every hour
export default async function Page({
params: { id },
}: {
params: { id: string };
}) {
const item = await getItem(id);
// ...
}
数据验证
数据验证
- 清除数据缓存, 重新获取数据;
验证策略
- 基于时间自动验证数据;
- 手动重新验证数据;
- 使用 revalidateTag() api;
- 定义 next config 中的 tag 标签;
- 使用 revalidateTag() 基于 tag 标签验证
- 使用 revalidatePath() api;
- 使用 revalidateTag() api;
// 基于时间自动验证数据 (秒)
fetch("https://...", { next: { revalidate: 3600 } });
// 使用 revalidatePath() api
("use server");
import { revalidatePath } from "next/cache";
export async function createPost() {
try {
// ...
} catch (error) {
// ...
}
revalidatePath("/posts");
}
// 使用 revalidateTag() api
export default async function Page() {
const res = await fetch("https://...", { next: { tags: ["collection"] } });
const data = await res.json();
// ...
}
("use server");
import { revalidateTag } from "next/cache";
export default async function action() {
revalidateTag("collection");
}
缓存交互
- Data Cache 的清空会作用于 Full Route Cache;
- Full Route Cache 的清空不会作用于 Data Cache;
- Data Cache 和 Route Cache;
- Route Handler 中 Data Cache 的清空不会作用于 Route Cache;
- 因为无法判断具体的路由片段;
- Server Action 中 Data Cache 的清空会作用于 Route Cache;
- Route Handler 中 Data Cache 的清空不会作用于 Route Cache;
API
| API | Router Cache | Full Route Cache | Data Cache | React Cache |
|---|---|---|---|---|
| Link prefetch | Cache | |||
| router.prefetch | Cache | |||
| router.refresh | Revalidate | |||
| fetch | Cache | Cache | ||
| fetch options.cache | Cache or Opt out | |||
| fetch options.next.revalidate | Revalidate | Revalidate | ||
| fetch options.next.tags | Cache | Cache | ||
| revalidateTag | Revalidate (Server Action) | Revalidate | Revalidate | |
| revalidatePath | Revalidate (Server Action) | Revalidate | Revalidate | |
| const revalidate | Revalidate or Opt out | Revalidate or Opt out | ||
| const dynamic | Cache or Opt out | Cache or Opt out | ||
| cookies | Revalidate (Server Action) | Opt out | ||
| headers, useSearchParams, searchParams | Opt out | |||
| generateStaticParams | Cache | |||
| React.cache | Cache | |||
| unstable_cache |