重新验证
缓存爽快,但也要能「让它过期」。Next.js 提供两条路线:基于时间 的 cacheLife,以及 按需 的 revalidateTag / updateTag / revalidatePath。本章讲清楚它们的区别与使用场景。
时间维度:cacheLife
cacheLife 在 use cache 作用域里指定缓存寿命:
import { cacheLife } from 'next/cache';
export async function getProducts() {
'use cache';
cacheLife('hours');
return db.query('SELECT * FROM products');
}
内置档位表:
| profile | stale | revalidate | expire |
|---|---|---|---|
seconds |
0 | 1s | 60s |
minutes |
5m | 1m | 1h |
hours |
5m | 1h | 1d |
days |
5m | 1d | 1w |
weeks |
5m | 1w | 30d |
max |
5m | 30d | ~永久 |
也可以传对象自定义:
cacheLife({
stale: 3600, // 1 小时后视为过时
revalidate: 7200, // 2 小时后重新生成
expire: 86400, // 1 天后彻底失效
});
Tag 维度:cacheTag
给一份缓存打标签,之后按标签批量失效:
import { cacheTag } from 'next/cache';
export async function getProducts() {
'use cache';
cacheTag('products');
return db.query('SELECT * FROM products');
}
revalidateTag:后台刷新(SWR)
适合「晚一点刷新也没问题」的场景(博客、商品目录)。stale-while-revalidate 语义:先把旧数据发出去,后台重新生成。
import { revalidateTag } from 'next/cache';
export async function updateUser() {
// 写数据…
revalidateTag('user', 'max'); // 'max' 允许最长的 stale 窗口
}
可在 Server Action 或 Route Handler 中调用。
updateTag:立即失效(读自己写)
适合用户「立刻看到自己刚写的东西」的场景:
import { updateTag } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createPost(formData: FormData) {
const post = await db.post.create({
data: { title: formData.get('title') },
});
updateTag('posts');
redirect(`/posts/${post.id}`);
}
只能在 Server Action 里用。
二者对比
updateTag |
revalidateTag |
|
|---|---|---|
| 使用场所 | 仅 Server Action | Server Action + Route Handler |
| 失效行为 | 立即过期 | stale-while-revalidate |
| 典型场景 | 读自己写 | 后台刷新 |
revalidatePath:按路径失效
不确定要失效哪些 tag 时的粗粒度方案:
import { revalidatePath } from 'next/cache';
export async function updateUser() {
// 写数据…
revalidatePath('/profile');
}
建议:优先使用 tag 失效,精准且不会「误伤」其他缓存。
缓存什么?
做一个简单的判断:
- 不依赖运行时数据(
cookies/headers/searchParams)且可以容忍一段时间旧?→ 缓存。 - 每次请求都必须最新?→ 不缓存,配
<Suspense>流式输出。 - CMS 内容?→ 用 tag + 长
cacheLife,发布后revalidateTag触发刷新。