首页 / Next.js 16 入门教程 / 数据重新验证

Next.js 16 入门教程

数据重新验证

本教程共 42 篇 · 第 12 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

Next.jsNext.js 16 入门教程重新验证cacheLiferevalidateTagISR

12. 数据重新验证

本节目标:学会两种重新验证策略——基于时间的自动刷新和按需手动刷新,掌握缓存生命周期的管理。

什么是重新验证

重新验证就是清除过期缓存,获取最新数据。Next.js 提供两种策略:

  1. 基于时间:缓存一段时间后自动失效
  2. 按需:数据变更时手动清除

基于时间的重新验证

cacheLife 设置缓存的有效期:

import { cacheLife } from 'next/cache'

export async function getProducts() {
  'use cache'
  cacheLife('hours') // 1 小时后重新验证
  return db.query('SELECT * FROM products')
}

预设配置

预设stalerevalidateexpire适用场景
default5 分钟15 分钟永不过期通用
seconds30 秒1 秒60 秒实时性要求高
minutes5 分钟1 分钟1 小时频繁变更
hours5 分钟1 小时1 天一般内容
days5 分钟1 天1 周很少变更
weeks5 分钟1 周30 天几乎不变
max5 分钟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}`)
}
updateTagrevalidateTag
使用位置仅 Server ActionsServer 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 RouterApp Router
revalidate: 60cacheLife('minutes')
getStaticPropsuse cache + generateStaticParams
res.revalidate()revalidateTag(tag, 'max') / revalidatePath()

什么该缓存

适合缓存的数据:

  • 不依赖运行时数据(cookies、headers)
  • 可以容忍一段时间的”不新鲜”
  • 多个用户看到相同内容

不适合缓存的数据:

  • 个性化内容(依赖用户身份)
  • 实时数据(股票、聊天)
  • 敏感数据(需要每次都验证权限)

小结

这一章我们学了数据重新验证的完整方案:

  1. cacheLife 设置基于时间的自动刷新
  2. revalidateTag 按标签清除(stale-while-revalidate)
  3. updateTag 立即过期(用户立刻看到变更)
  4. revalidatePath 按路径清除
  5. 优先用标签,比路径更精确

下一章,我们来学习错误处理。