前面几节我们学习了数据获取和数据变更,本节来深入了解 Next.js 的缓存机制。缓存是提升应用性能的关键技术,用好了能让你的应用速度提升好几倍。
缓存架构
Next.js 使用多层缓存来优化性能,理解这个架构很重要:
请求流程:
浏览器 → CDN/边缘缓存 → Next.js 数据缓存 → 数据源
缓存层级
- 浏览器缓存 - 客户端存储
- CDN/边缘缓存 - Vercel Edge Network
- Next.js 数据缓存 - 服务器端缓存
- 完全动态请求 - 每次都获取新数据
每一层缓存都有自己的作用,合理配置能让应用性能最优。
数据缓存
默认缓存行为
在 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 的缓存机制,包括多层缓存架构、基于标签和路径的缓存、不同的缓存策略等。合理使用缓存能让你的应用性能大幅提升,但也要注意根据数据特性选择合适的缓存策略。
如果你对本节内容有任何疑问,欢迎在评论区提出来,我们一起学习讨论。