理解 Next.js 缓存(第5部分)
Source: Dev.to
面包店类比(API 数据 vs 页面)
把你的 Next.js 应用想象成一家面包店。
- API 数据(数据缓存) 就是从数据库或外部 API 获取的原料(面粉、糖、鸡蛋)。
- 页面(完整路由缓存) 就是摆在橱窗里的成品蛋糕。
Next.js 不仅缓存生成好的 HTML(蛋糕),还会缓存原始的 JSON(原料)。这意味着如果 50 位用户在同一时刻访问你的网站,Next.js 不会对数据库发起 50 次查询——它会使用缓存的原料立即为所有人提供服务。
四层缓存
最常让人困惑的点是用户点击刷新按钮时会发生什么。下面用厨房的比喻展示 Next.js 的四层缓存。
1. 请求记忆化(台面/剪贴板)
如果同一页面上的三个不同 React 组件都调用 fetch('/api/user'),Next.js 会去重请求:只发起一次网络请求并共享结果。
注意: 台面/剪贴板会在每一次页面渲染后被清空。
2. 路由缓存(顾客的盘子)
当用户在应用中导航时,Next.js 会把 React Server Component 的负载存入浏览器内存。这使得点击 返回 按钮时几乎是瞬间完成。
注意: 该缓存不会在硬刷新后保留;刷新会清空浏览器的内存(洗掉盘子)。
3. 数据缓存(储藏室)
Next.js 会在 多个 用户会话之间存储从数据库返回的原始 JSON 数据。
注意: 这在刷新后仍然存在。当页面刷新时,浏览器会向服务器请求新数据,但服务器会直接从储藏室取出同一份缓存数据。这也是刷新往往不能“修复”陈旧数据的原因。
4. 完整路由缓存(橱窗)
Next.js 将缓存的数据和你的组件在构建时渲染成纯静态 HTML。
注意: 只要底层的 数据缓存 未被显式失效,这个缓存会在刷新后仍然生效,因为它会一直提供预渲染的 HTML 文件。
控制缓存
缓存对性能非常有利,但动态应用需要动态数据。下面介绍如何让 Next.js 丢弃陈旧的原料。
1. 完全关闭缓存(不缓存)
如果某段数据经常变化(例如实时股票行情或银行账户余额),可以告诉 Next.js 完全不缓存 fetch 请求:
// 每一次请求都重新获取
const res = await fetch('https://api.example.com/data', {
cache: 'no-store',
});2. 基于时间的重新验证(Stale‑While‑Revalidate)
让 Next.js 在一定时间内保留缓存,之后在后台获取新数据:
// 每 3600 秒(1 小时)重新验证一次数据
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 },
});3. 按需重新验证(“失效”按钮)
当用户提交表单(例如更新个人资料)时,需要立即清除该页面的缓存。可以在 Server Action 中这样做:
'use server';
import { revalidatePath } from 'next/cache';
export async function updateProfile(formData) {
await db.user.update(formData);
// 告诉 Next.js 丢弃该路由的缓存 HTML 和数据
revalidatePath('/profile');
}大结局
在这五篇文章中,我们从“新鲜 vs 陈旧”的理论出发,逐步掌握了 HTTP 头、调教 React‑Query/Redux 客户端状态,最终征服了 Next.js 的服务器缓存。
缓存不再只是后端的性能优化——它是前端用户体验的核心跳动。掌握它,你就能构建出即刻响应且节省服务器成本的应用。
现在,去构建一些快速的东西吧。