Next.js第八课 - 缓存机制

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

缓存架构

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

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

缓存层级

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

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

数据缓存

默认缓存行为

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

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

缓存选项

禁用缓存

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

tsx 复制代码
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>
}

设置缓存时间

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

tsx 复制代码
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>
}

基于标签的缓存

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

添加缓存标签

tsx 复制代码
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} />
}

重新验证标签

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

tsx 复制代码
'use server'

import { revalidateTag } from 'next/cache'

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

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

多个标签

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

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

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

基于路径的缓存

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

tsx 复制代码
'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('/') // 首页也可能显示文章
}

时间基础重新验证

页面级配置

整个页面设置缓存时间:

tsx 复制代码
// app/posts/page.tsx
export const revalidate = 3600 // 1 小时

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

特定请求配置

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

tsx 复制代码
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()
}

缓存策略

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

静态内容

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

tsx 复制代码
// 适合:博客文章、产品页面
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} />
}

动态内容

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

tsx 复制代码
// 适合:用户数据、实时统计
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} />
}

按需重新验证

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

tsx 复制代码
// 适合:内容更新不频繁
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 })
}

缓存实用技巧

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

根据数据特性选择策略

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

tsx 复制代码
// 静态内容可以长时间缓存
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',
})

使用标签进行精确控制

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

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

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

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

分层缓存策略

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

tsx 复制代码
// 页面级别使用较长缓存
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>
}

不同场景的缓存策略

电商网站

tsx 复制代码
// 产品列表 - 中等缓存
export const revalidate = 1800 // 30 分钟

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

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

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

新闻网站

tsx 复制代码
// 文章列表 - 短缓存
export const revalidate = 300 // 5 分钟

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

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

社交媒体

tsx 复制代码
// 用户资料 - 中等缓存
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

相关推荐
Melrose2 小时前
移动端安全攻防
android·前端·安全
不想说话的麋鹿2 小时前
「性能优化」《从10秒到100ms:大文件上传极致优化实战(切片/秒传/断点续传全方案)》
前端·vue.js·性能优化
梵得儿SHI2 小时前
Vue 3 工程化实战:Axios 高阶封装与样式解决方案深度指南
前端·javascript·vue3·axios·样式解决方案·api请求管理·统一请求处理
烈风2 小时前
01_Tauri环境搭建
开发语言·前端·后端
暗不需求2 小时前
深入 JavaScript 核心:用原生 JavaScript 打造就地编辑组件
前端·javascript
一只叁木Meow2 小时前
Vite+:前端开发的"超级管家"来了
前端
不可能的是2 小时前
浏览器端音频转码实战:FFmpeg.wasm 深度定制与踩坑指南
前端
南风知我意9572 小时前
【重构思维】用位运算做权限管理
前端·面试·职场和发展·性能优化·重构
江湖行骗老中医2 小时前
Vue 3 的父子组件传值主要遵循单向数据流的原则:父传子 和 子传父。
前端·javascript·vue.js