Next.js 16 入门教程
数据重新验证
本教程共 42 篇 · 第 12 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
Next.jsNext.js 16 入门教程重新验证cacheLiferevalidateTagISR
12. 数据重新验证
本节目标:学会两种重新验证策略——基于时间的自动刷新和按需手动刷新,掌握缓存生命周期的管理。
什么是重新验证
重新验证就是清除过期缓存,获取最新数据。Next.js 提供两种策略:
- 基于时间:缓存一段时间后自动失效
- 按需:数据变更时手动清除
基于时间的重新验证
用 cacheLife 设置缓存的有效期:
import { cacheLife } from 'next/cache'
export async function getProducts() {
'use cache'
cacheLife('hours') // 1 小时后重新验证
return db.query('SELECT * FROM products')
}
预设配置
| 预设 | stale | revalidate | expire | 适用场景 |
|---|---|---|---|---|
default | 5 分钟 | 15 分钟 | 永不过期 | 通用 |
seconds | 30 秒 | 1 秒 | 60 秒 | 实时性要求高 |
minutes | 5 分钟 | 1 分钟 | 1 小时 | 频繁变更 |
hours | 5 分钟 | 1 小时 | 1 天 | 一般内容 |
days | 5 分钟 | 1 天 | 1 周 | 很少变更 |
weeks | 5 分钟 | 1 周 | 30 天 | 几乎不变 |
max | 5 分钟 | 30 天 | 1 年 | 永久内容 |
自定义配置
'use cache'
cacheLife({
stale: 3600, // 1 小时后标记为过期
revalidate: 7200, // 2 小时后必须重新验证
expire: 86400, // 1 天后彻底失效
})
Note短生命周期缓存(
seconds预设、revalidate: 0、或expire低于 5 分钟)会被排除在预渲染之外,变成动态内容。
按需重新验证
数据变更时,手动清除缓存。
revalidateTag
按标签清除缓存,使用 stale-while-revalidate 语义:
import { revalidateTag } from 'next/cache'
export async function updateUser(id: string) {
// 更新数据
await db.update(users).where({ id })
// 清除缓存
revalidateTag('users', 'max')
}
第二个参数控制旧数据可以服务多久:
'max':最长 stale 窗口(推荐)- 自定义秒数
updateTag
立即过期缓存,用户立刻看到变更:
import { updateTag } from 'next/cache'
export async function createPost(formData: FormData) {
const post = await db.post.create({
data: { /* ... */ }
})
updateTag('posts')
redirect(`/posts/${post.id}`)
}
updateTag | revalidateTag | |
|---|---|---|
| 使用位置 | 仅 Server Actions | Server Actions + Route Handlers |
| 行为 | 立即过期 | 先返回旧数据,后台刷新 |
| 场景 | 用户要立刻看到自己的变更 | 允许短暂延迟 |
revalidatePath
按路径清除缓存:
import { revalidatePath } from 'next/cache'
export async function updateUser(id: string) {
await db.update(users).where({ id })
revalidatePath('/profile')
}
Tip优先用标签重新验证(
revalidateTag/updateTag),比路径更精确,不会过度清除。
给缓存打标签
在 use cache 作用域内调用 cacheTag:
import { cacheTag } from 'next/cache'
export async function getProducts() {
'use cache'
cacheTag('products')
cacheLife('hours')
return db.query('SELECT * FROM products')
}
export async function getProduct(id: string) {
'use cache'
cacheTag('products') // 同一个标签
cacheLife('hours')
return db.query('SELECT * FROM products WHERE id = ?', id)
}
调用 revalidateTag('products', 'max') 会同时清除列表和详情缓存。
多个标签
可以给同一个缓存打多个标签:
export async function getProduct(id: string) {
'use cache'
cacheTag('products')
cacheTag(`product:${id}`)
cacheLife('hours')
return db.query('SELECT * FROM products WHERE id = ?', id)
}
这样既可以批量清除所有产品缓存,也可以单独清除某个产品。
在 Route Handler 中重新验证
Route Handler 也可以触发重新验证:
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'
import { NextRequest } from 'next/server'
export async function POST(request: NextRequest) {
const tag = request.nextUrl.searchParams.get('tag')
if (tag) {
revalidateTag(tag, 'max')
return Response.json({ revalidated: true, now: Date.now() })
}
return Response.json({ revalidated: false })
}
外部系统(比如 CMS 的 webhook)可以调用这个接口触发重新验证。
ISR 迁移
如果你从 Pages Router 的 ISR 迁移过来:
| Pages Router | App Router |
|---|---|
revalidate: 60 | cacheLife('minutes') |
getStaticProps | use cache + generateStaticParams |
res.revalidate() | revalidateTag(tag, 'max') / revalidatePath() |
什么该缓存
适合缓存的数据:
- 不依赖运行时数据(cookies、headers)
- 可以容忍一段时间的”不新鲜”
- 多个用户看到相同内容
不适合缓存的数据:
- 个性化内容(依赖用户身份)
- 实时数据(股票、聊天)
- 敏感数据(需要每次都验证权限)
小结
这一章我们学了数据重新验证的完整方案:
cacheLife设置基于时间的自动刷新revalidateTag按标签清除(stale-while-revalidate)updateTag立即过期(用户立刻看到变更)revalidatePath按路径清除- 优先用标签,比路径更精确
下一章,我们来学习错误处理。