Next.js第八课 - 缓存机制

0 阅读5分钟

前面几节我们学习了数据获取和数据变更,本节来深入了解 Next.js 的缓存机制。缓存是提升应用性能的关键技术,用好了能让你的应用速度提升好几倍。

缓存架构

Next.js 使用多层缓存来优化性能,理解这个架构很重要:

请求流程:
浏览器 → CDN/边缘缓存 → Next.js 数据缓存 → 数据源

缓存层级

  1. 浏览器缓存 - 客户端存储
  2. CDN/边缘缓存 - Vercel Edge Network
  3. Next.js 数据缓存 - 服务器端缓存
  4. 完全动态请求 - 每次都获取新数据

每一层缓存都有自己的作用,合理配置能让应用性能最优。

数据缓存

默认缓存行为

在 Next.js 中,fetch 请求默认会被缓存:

// 默认缓存
export default async function Page() {
  const res = await fetch('https://api.example.com/data')
  const data = await res.json()
  return <div>{data.title}</div>
}

缓存选项

禁用缓存

如果需要实时数据,可以完全禁用缓存:

export default async function Page() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store',
  })
  const data = await res.json()
  return <div>{data.title}</div>
}

设置缓存时间

最常用的是设置缓存过期时间:

export default async function Page() {
  // 缓存 10 秒
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 10 },
  })
  const data = await res.json()
  return <div>{data.title}</div>
}

基于标签的缓存

标签缓存是一个很强大的特性,它允许你按标签批量清除缓存。

添加缓存标签

export default async function PostsPage() {
  const posts = await fetch('https://api.example.com/posts', {
    next: { tags: ['posts', 'blog'] },
  }).then((res) => res.json())

  return <PostList posts={posts} />
}

重新验证标签

当数据更新时,可以按标签清除缓存:

'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
  // 创建新文章
  await db.post.create({ /* ... */ })

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

多个标签

一个请求可以有多个标签:

// 获取时添加多个标签
const data = await fetch('https://api.com/dashboard', {
  next: { tags: ['dashboard', 'analytics', 'user-123'] },
})

// 可以单独重新验证
revalidateTag('dashboard')
revalidateTag('analytics')

基于路径的缓存

除了标签,还可以按路径重新验证缓存:

'use server'

import { revalidatePath } from 'next/cache'

export async function updatePost(id: string, data: FormData) {
  // 更新文章
  await db.post.update({
    where: { id },
    data: { title: data.get('title') },
  })

  // 重新验证相关页面
  revalidatePath('/posts')
  revalidatePath(`/posts/${id}`)
  revalidatePath('/') // 首页也可能显示文章
}

时间基础重新验证

页面级配置

整个页面设置缓存时间:

// app/posts/page.tsx
export const revalidate = 3600 // 1 小时

export default async function PostsPage() {
  const posts = await getPosts()
  return <PostList posts={posts} />
}

特定请求配置

不同请求使用不同的缓存策略:

async function getFreshData() {
  const res = await fetch('https://api.com/data', {
    next: { revalidate: 0 },
  })
  return res.json()
}

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

缓存策略

根据数据特性选择合适的缓存策略:

静态内容

适合博客文章、产品页面等不经常变化的内容:

// 适合:博客文章、产品页面
export const revalidate = 3600 // 1 小时

export default async function BlogPage() {
  const posts = await fetch('https://api.com/posts', {
    next: { revalidate: 3600 },
  }).then((res) => res.json())

  return <PostList posts={posts} />
}

动态内容

适合用户数据、实时统计等需要最新数据的内容:

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

export default async function DashboardPage() {
  const stats = await fetch('https://api.com/stats', {
    cache: 'no-store',
  }).then((res) => res.json())

  return <Stats data={stats} />
}

按需重新验证

适合内容更新不频繁但需要快速生效的场景:

// 适合:内容更新不频繁
export const revalidate = 86400 // 24 小时

// 当内容更新时手动触发
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'

export async function POST(request: Request) {
  const { tag } = await request.json()
  revalidateTag(tag)
  return Response.json({ revalidated: true })
}

缓存实用技巧

这里分享几个在缓存配置中特别实用的技巧。

根据数据特性选择策略

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

// 静态内容可以长时间缓存
export const revalidate = 86400 // 24 小时
const staticData = await fetch('https://api.com/static', {
  next: { revalidate: 86400 },
})

// 频繁更新的内容建议短时间缓存
const frequentlyUpdated = await fetch('https://api.com/frequent', {
  next: { revalidate: 60 },
})

// 实时数据最好不缓存
const realtimeData = await fetch('https://api.com/realtime', {
  cache: 'no-store',
})

使用标签进行精确控制

这个技巧在实际项目中特别有用——使用标签可以更精确地控制缓存:

// 推荐这样做 - 使用标签进行精确控制
const posts = await fetch('https://api.com/posts', {
  next: { tags: ['posts', 'blog'] },
})

// 当创建新文章时,只重新验证相关缓存
revalidateTag('posts')

// 尽量避免这种情况 - 使用路径可能重新验证过多内容
revalidatePath('/posts')

分层缓存策略

这里有个小建议:不同层级可以使用不同的缓存时间,这样更灵活:

// 页面级别使用较长缓存
export const revalidate = 3600

// 组件级别使用较短缓存
async function PostList() {
  const posts = await fetch('https://api.com/posts', {
    next: { revalidate: 600 },
  })
  return <div>{/* ... */}</div>
}

// 实时数据调用不缓存
async function getUserActivity() {
  const activity = await fetch('https://api.com/activity', {
    cache: 'no-store',
  })
  return <div>{/* ... */}</div>
}

不同场景的缓存策略

电商网站

// 产品列表 - 中等缓存
export const revalidate = 1800 // 30 分钟

// 产品详情 - 短缓存(库存可能变化)
export const revalidate = 60 // 1 分钟

// 用户购物车 - 不缓存
export const dynamic = 'force-dynamic'

// 静态内容(关于页面等)- 长缓存
export const revalidate = 86400 // 24 小时

新闻网站

// 文章列表 - 短缓存
export const revalidate = 300 // 5 分钟

// 文章详情 - 发布后不变化
export const revalidate = 86400 // 24 小时

// 实时新闻 - 不缓存
export const dynamic = 'force-dynamic'

社交媒体

// 用户资料 - 中等缓存
export const revalidate = 600 // 10 分钟

// 用户帖子 - 短缓存
export const revalidate = 60 // 1 分钟

// 通知 - 不缓存
export const dynamic = 'force-dynamic'

总结

本节我们深入了解了 Next.js 的缓存机制,包括多层缓存架构、基于标签和路径的缓存、不同的缓存策略等。合理使用缓存能让你的应用性能大幅提升,但也要注意根据数据特性选择合适的缓存策略。

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

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