用途
Cache Components 是 Next.js 16 引入的缓存机制,用于实现部分预渲染(PPR,Partial Prerendering),让同一个路由中同时包含静态、缓存和动态内容。它取代了旧版本中的 experimental.ppr 标志。
启用方式
在 next.config.ts 中设置:
import type {NextConfig} from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
三类内容
启用后,页面内容分为三类:
- 静态(自动预渲染):同步代码、导入、纯计算,在构建时预渲染。
- 缓存(use cache):不需要每次请求都重新获取的异步数据。
- 动态(Suspense):必须保持新鲜的运行时数据,用 Suspense 包裹。
示例:
import {Suspense} from 'react'
export default function Page() {
return (
<>
<BlogPosts /> {/* Cached */}
<Suspense fallback={<p>Loading...</p>}>
<UserPreferences /> {/* Dynamic - streams in */}
</Suspense>
</>
)
}
async function UserPreferences() {
const theme = (await cookies()).get('theme')?.value
return <p>Theme: {theme}</p>
}
use cache 指令
可作用于三个层级:
- 文件级:在文件顶部写 'use cache',整个页面被缓存。
- 组件级:在异步组件内写 'use cache'。
- 函数级:在异步函数内写 'use cache'。
缓存配置
内置配置:
- 'use cache':默认,5 分钟 stale,15 分钟 revalidate
- 'use cache: remote':平台提供的缓存(Redis、KV)
- 'use cache: private':用于合规场景,允许运行时 API
cacheLife() 可设置自定义生命周期:
import {cacheLife} from 'next/cache'
async function getData() {
'use cache'
cacheLife('hours')
return fetch('/api/data')
}
内置配置包括:'default'、'minutes'、'hours'、'days'、'weeks'、'max'。也可内联配置:
cacheLife({
stale: 3600, // 1 hour - serve stale while revalidating
revalidate: 7200, // 2 hours - background revalidation interval
expire: 86400, // 1 day - hard expiration
})
缓存失效
- cacheTag():为缓存内容打标签。
- updateTag():立即失效,同一请求内即可看到新数据。
- revalidateTag():后台重新验证,下一次请求看到新数据(stale-while-revalidate)。
示例:
'use server'
import {updateTag} from 'next/cache'
export async function updateProduct(id: string, data: FormData) {
await db.products.update({where: {id}, data})
updateTag(`product-${id}`)
}
运行时数据约束
在 use cache 内不能访问 cookies()、headers() 或 searchParams。解决方案是把这些值提取到外部,作为参数传入,参数会自动成为缓存键的一部分。
例外:使用 'use cache: private' 时允许访问运行时 API,适用于无法重构的合规场景。
缓存键生成
缓存键自动基于以下内容生成:
- Build ID:部署时使所有缓存失效
- Function ID:函数位置的哈希
- 可序列化参数:props 成为键的一部分
- 闭包变量:外层作用域的值也会被包含
完整示例
import {Suspense} from 'react'
import {cookies} from 'next/headers'
import {cacheLife, cacheTag} from 'next/cache'
export default function DashboardPage() {
return (
<>
<header>
<h1>Dashboard</h1>
</header>
<nav>...</nav>
<Stats />
<Suspense fallback={<NotificationsSkeleton />}>
<Notifications />
</Suspense>
</>
)
}
async function Stats() {
'use cache'
cacheLife('hours')
cacheTag('dashboard-stats')
const stats = await db.stats.aggregate()
return <StatsDisplay stats={stats} />
}
async function Notifications() {
const userId = (await cookies()).get('userId')?.value
const notifications = await db.notifications.findMany({
where: {userId, read: false},
})
return <NotificationList items={notifications} />
}
从旧版本迁移
| 旧配置 | 替代方案 |
|---|---|
| experimental.ppr | cacheComponents: true |
| dynamic = 'force-dynamic' | 移除(默认行为) |
| dynamic = 'force-static' | 'use cache' + cacheLife('max') |
| revalidate = N | cacheLife({ revalidate: N }) |
| unstable_cache() | 'use cache' 指令 |
从 unstable_cache 迁移到 use cache
unstable_cache 在 Next.js 16 中已被 use cache 指令取代。启用 cacheComponents 后,将 unstable_cache 调用转换为 use cache 函数。
迁移前:
import {unstable_cache} from 'next/cache'
const getCachedUser = unstable_cache(async (id) => getUser(id), ['my-app-user'], {
tags: ['users'],
revalidate: 60,
})
迁移后:
import {cacheLife, cacheTag} from 'next/cache'
async function getCachedUser(id: string) {
'use cache'
cacheTag('users')
cacheLife({revalidate: 60})
return getUser(id)
}
主要差异:
- 无需手动缓存键:use cache 会根据函数参数和闭包自动生成键,不再需要 unstable_cache 的 keyParts 数组。
- 标签:用函数内的 cacheTag() 调用替代 options.tags。
- 重新验证:用 cacheLife({ revalidate: N }) 或内置配置(如 cacheLife('minutes'))替代 options.revalidate。
- 动态数据:unstable_cache 不支持在回调内使用 cookies() 或 headers(),use cache 有相同限制,但必要时可使用 'use cache: private'。
限制
- 不支持 Edge runtime,需要 Node.js。
- 不支持静态导出,需要服务器。
- 非确定性值(Math.random()、Date.now())在 use cache 内只在构建时执行一次。
如需在缓存外获得请求时随机性:
import {connection} from 'next/server'
async function DynamicContent() {
await connection() // Defer to request time
const id = crypto.randomUUID() // Different per request
return <div>{id}</div>
}