Next.js第九课 - 缓存重新验证

12 阅读4分钟

上节我们了解了缓存机制,本节来深入学习缓存重新验证。重新验证是指在缓存过期后如何获取最新数据,这是保证数据新鲜度的关键机制。

重新验证概述

重新验证(Revalidation)是指清除缓存并获取最新数据的过程。Next.js 提供两种重新验证方式:

  1. 基于时间 - 设定时间间隔自动重新验证
  2. 按需 - 通过事件触发重新验证

理解这两种方式的区别和使用场景很重要。

基于时间的重新验证

设置重新验证间隔

最简单的方式是设置固定的重新验证时间:

// app/products/page.tsx
export const revalidate = 3600 // 每 1 小时重新验证

export default async function ProductsPage() {
  const products = await getProducts()

  return <ProductList products={products} />
}

使用 next.revalidate

更灵活的方式是在 fetch 请求中设置:

async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    next: { revalidate: 3600 },
  })
  return res.json()
}

export default async function ProductsPage() {
  const products = await getProducts()
  return <ProductList products={products} />
}

重新验证工作原理

基于时间的重新验证使用的是 Stale-While-Revalidate 策略:

T = 0  → 用户访问,数据被缓存
T = 1  → 用户访问,返回缓存数据(背景重新验证)
T = 2  → 用户访问,返回新数据

这意味着用户永远不会等待,总是能快速得到响应,同时数据也能保持相对新鲜。

按需重新验证

有时候我们希望在数据更新时立即重新验证,而不是等待缓存过期。这就是按需重新验证。

revalidatePath

根据特定路径重新验证缓存:

'use server'

import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData) {
  const post = await db.post.create({
    data: {
      title: formData.get('title'),
      content: formData.get('content'),
    },
  })

  // 重新验证文章列表页面
  revalidatePath('/posts')

  // 重新验证首页(如果显示文章)
  revalidatePath('/')

  return post
}

revalidateTag

根据标签重新验证缓存,这种方式更加灵活:

'use server'

import { revalidateTag } from 'next/cache'

export async function updatePost(id: string, data: any) {
  const post = await db.post.update({
    where: { id },
    data,
  })

  // 重新验证所有带 'posts' 标签的缓存
  revalidateTag('posts')

  return post
}

多个标签

一个数据更新可能影响多个缓存:

'use server'

import { revalidateTag } from 'next/cache'

export async function createComment() {
  await db.comment.create(/* ... */)

  // 重新验证多个缓存
  revalidateTag('comments')
  revalidateTag('posts') // 更新文章评论数
  revalidateTag('user-activity')
}

后台重新验证

Next.js 支持后台重新验证,这意味着在重新验证时,用户仍然可以得到缓存的旧数据,而新数据在后台获取。

配置后台重新验证

export default async function Page() {
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 3600 },
  })
  const data = await res.json()

  return <div>{/* ... */}</div>
}

这样配置后,第一次访问会获取并缓存数据。后续访问会返回缓存数据,同时在后台重新验证。如果重新验证成功,下次访问就会返回新数据。

路由段配置

revalidate

设置页面的重新验证时间(秒):

// 每 1 小时重新验证
export const revalidate = 3600

dynamic

控制页面是静态还是动态渲染:

// 强制动态渲染(不缓存)
export const dynamic = 'force-dynamic'

// 强制静态渲染
export const dynamic = 'force-static'

// 自动选择(默认)
export const dynamic = 'auto'

重新验证策略

选择合适的重新验证策略很重要:

静态内容

适合不经常变化的内容:

// 博客文章、文档
export const revalidate = 86400 // 24 小时

动态内容

适合经常变化的内容:

// 用户数据、实时统计
export const dynamic = 'force-dynamic'

混合策略

同一个应用中不同部分使用不同策略:

// 文章列表 - 每 10 分钟更新
export const revalidate = 600

// 文章详情 - 每小时更新
export const revalidate = 3600

// 用户数据 - 实时
export const dynamic = 'force-dynamic'

实用建议

这里分享几个在缓存重新验证时特别实用的技巧。

根据数据更新频率选择策略

实际开发中,根据数据更新频率选择合适的策略特别重要:

// 不经常变化的数据可以长时间缓存
export const revalidate = 86400

// 经常变化的数据建议短时间缓存
export const revalidate = 60

// 实时数据最好不缓存
export const dynamic = 'force-dynamic'

使用标签进行精确控制

这个技巧特别有用——使用标签可以更精确地控制缓存重新验证:

// 获取数据时添加标签
const posts = await fetch('https://api.com/posts', {
  next: { tags: ['posts', 'blog'] },
})

// 更新时按标签重新验证,更灵活
revalidateTag('posts')

避免过度重新验证

这里有个小建议:不要频繁重新验证不变的数据,这样会浪费资源:

// 避免这种情况 - 每秒重新验证太频繁
export const revalidate = 1

// 推荐这样做 - 选择合理的时间间隔
export const revalidate = 3600

总结

本节我们深入了解了缓存重新验证机制,包括基于时间和按需重新验证、后台重新验证、不同的重新验证策略等。合理使用重新验证能让你的应用既快速又能保持数据新鲜。

如果你对本节内容有任何疑问,欢迎在评论区提出来,我们一起学习讨论。

原文地址:https://blog.uuhb.cn/archives/next-js-09.html