Next.js 16 入门教程
高级缓存策略
本教程共 42 篇 · 第 39 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
Next.jsNext.js 16 入门教程缓存cacheLifeCDNstale-while-revalidateISR
39. 高级缓存策略
本节目标:掌握 Next.js 的高级缓存配置,理解 cacheLife 的三个时间维度,学会设计合理的缓存生命周期。
缓存生命周期(cacheLife)
Next.js 16 引入了 cacheLife 配置,用三个时间维度精确控制缓存行为:
| 属性 | 说明 |
|---|---|
stale | 客户端缓存多久不检查服务端 |
revalidate | 服务端多久重新验证一次 |
expire | 过期数据最多保留多久,之后转为动态渲染 |
这三个时间的关系:stale < revalidate < expire
配置自定义缓存 Profile
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
blog: {
stale: 3600, // 客户端缓存 1 小时
revalidate: 900, // 服务端每 15 分钟重新验证
expire: 86400, // 最多保留 1 天
},
},
}
export default nextConfig
在代码中使用:
import { cacheLife } from 'next/cache'
export async function getBlogPosts() {
'use cache'
cacheLife('blog')
const data = await fetch('/api/posts')
return data
}
工作流程
- 用户 A 请求页面,数据被缓存
- 接下来 1 小时内(stale),用户 A 再次访问直接用缓存,不发请求
- 15 分钟后(revalidate),用户 B 访问时返回过期缓存,后台重新验证
- 1 天后(expire),缓存彻底过期,下次请求实时渲染
不使用 Cache Components 的缓存
如果你的项目还没启用 cacheComponents,可以用传统的缓存方式:
fetch 缓存
// 强制缓存
const data = await fetch('https://...', { cache: 'force-cache' })
// 设置重新验证时间
const data = await fetch('https://...', { next: { revalidate: 3600 } })
unstable_cache
缓存非 fetch 的异步函数:
import { unstable_cache } from 'next/cache'
export const getCachedUser = unstable_cache(
async (id: string) => {
return db.select().from(users).where(eq(users, id))
},
['user'], // 缓存键
{
tags: ['user'], // 用于按需重新验证
revalidate: 3600, // 1 小时后过期
}
)
路由段配置
// 强制动态渲染
export const dynamic = 'force-dynamic'
// 强制静态渲染
export const dynamic = 'force-static'
// 设置默认重新验证时间
export const revalidate = 3600
按需重新验证
revalidatePath 和 revalidateTag 的完整用法见 §12。这里补充一点:在不启用 Cache Components 时,可以给 fetch 请求打标签,再用 revalidateTag 按需刷新:
// 给 fetch 请求打标签(非 Cache Components 场景)
const data = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
})
// 数据变更后按标签重新验证
revalidateTag('posts', 'max')
请求去重
在同一个渲染过程中,相同的请求会自动去重。但如果用 ORM 直接查数据库,需要手动处理:
import { cache } from 'react'
export const getPost = cache(async (id: string) => {
return await db.query.posts.findFirst({
where: eq(posts.id, parseInt(id)),
})
})
预加载数据
用 preload 模式提前发起数据请求:
import { cache } from 'react'
import 'server-only'
export const getItem = cache(async (id: string) => {
// 查询逻辑
})
export const preload = (id: string) => {
void getItem(id) // 不等待结果,只触发请求
}
export default async function Page({ params }) {
const { id } = await params
preload(id) // 提前开始加载
const isAvailable = await checkIsAvailable() // 做其他事
return isAvailable ? <Item id={id} /> : null
}
CDN 缓存
Next.js 的缓存响应会自动带上 cache-control 头,CDN 可以根据这些头做缓存:
- 静态页面:
public, max-age=31536000, immutable - ISR 页面:
public, max-age=0, s-maxage=60, stale-while-revalidate
自定义缓存头
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/blog/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=3600, s-maxage=86400',
},
],
},
]
},
}
export default nextConfig
缓存调试
设置环境变量可以看到缓存命中情况:
NEXT_PRIVATE_DEBUG_CACHE=1
响应头 x-nextjs-cache 的值:
HIT:命中缓存STALE:返回过期缓存,后台重新验证MISS:未命中,重新渲染REVALIDATED:按需重新验证
最佳实践
- 静态内容用长缓存:营销页面、文档可以缓存很久
- 用户数据用短缓存:个人信息、购物车要频繁更新
- 用标签做精细控制:不要整个站点一起重新验证
- 监控缓存命中率:太低说明缓存策略有问题
- 多实例部署用共享缓存:文件系统缓存是单实例的
缓存是性能优化的核心手段。理解 stale-while-revalidate 模型,能帮你在数据新鲜度和响应速度之间找到最佳平衡。