'use cache: remote' 指令可在动态上下文(例如在调用 await connection()、await cookies() 或 await headers() 之后)中启用共享数据的缓存,而常规的 use cache 在这些情况下将不起作用。
值得注意的是:
- 结果存储在服务器端缓存处理程序中,并跨所有用户共享。
- 对于依赖于
await cookies()或await headers()的用户特定数据,请改用'use cache: private'。
要使用 'use cache: remote',请在您的 next.config.ts 文件中启用 cacheComponents 标志:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig/** @type {import('next').NextConfig} */
const nextConfig = {
cacheComponents: true,
}
export default nextConfig然后将 'use cache: remote' 添加到需要动态上下文缓存数据的函数中。
缓存需要在请求时获取但可以跨所有用户共享的产品定价。使用 cacheLife 来设置价格的缓存生命周期。
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cacheTag, cacheLife } from 'next/cache'
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return (
<div>
<ProductDetails id={id} />
<Suspense fallback={<div>Loading price...</div>}>
<ProductPrice productId={id} />
</Suspense>
</div>
)
}
function ProductDetails({ id }: { id: string }) {
return <div>Product: {id}</div>
}
async function ProductPrice({ productId }: { productId: string }) {
// Calling connection() makes this component dynamic, preventing
// it from being included in the static shell. This ensures the price
// is always fetched at request time.
await connection()
// Now we can cache the price in a remote cache handler.
// Regular 'use cache' would NOT work here because we're in a dynamic context.
const price = await getProductPrice(productId)
return <div>Price: ${price}</div>
}
async function getProductPrice(productId: string) {
'use cache: remote'
cacheTag(`product-price-${productId}`)
cacheLife({ expire: 3600 }) // 1 hour
// This database query is cached and shared across all users
return db.products.getPrice(productId)
}import { Suspense } from 'react'
import { connection } from 'next/server'
import { cacheTag, cacheLife } from 'next/cache'
export default async function ProductPage({ params }) {
const { id } = await params
return (
<div>
<ProductDetails id={id} />
<Suspense fallback={<div>Loading price...</div>}>
<ProductPrice productId={id} />
</Suspense>
</div>
)
}
function ProductDetails({ id }) {
return <div>Product: {id}</div>
}
async function ProductPrice({ productId }) {
// Calling connection() makes this component dynamic, preventing
// it from being included in the static shell. This ensures the price
// is always fetched at request time.
await connection()
// Now we can cache the price in a remote cache handler.
// Regular 'use cache' would NOT work here because we're in a dynamic context.
const price = await getProductPrice(productId)
return <div>Price: ${price}</div>
}
async function getProductPrice(productId) {
'use cache: remote'
cacheTag(`product-price-${productId}`)
cacheLife({ expire: 3600 }) // 1 hour
// This database query is cached and shared across all users
return db.products.getPrice(productId)
}注意: 常规的
use cache在动态上下文中使用时(在await connection()、await cookies()、await headers()等之后)将不会缓存任何内容。在这些场景中,请使用'use cache: remote'来启用运行时缓存。
'use cache: remote' 与 use cache 和 'use cache: private' 有何不同Next.js 提供了三种缓存指令,每种都专为不同的用例设计:
| 特性 | use cache | 'use cache: remote' | 'use cache: private' |
|---|---|---|---|
| 动态上下文支持 | 否(需要静态上下文) | 是(专为动态上下文设计) | 是 |
访问 await cookies() | 否 | 否 | 是 |
访问 await headers() | 否 | 否 | 是 |
在 await connection() 之后 | 否(不会缓存) | 否 | 否 |
| 存储在缓存处理程序中 | 是(服务器端) | 是(服务器端) | 否(仅客户端) |
| 缓存范围 | 全局(共享) | 全局(共享) | 按用户(隔离) |
| 支持运行时预取 | 不适用(在构建时预渲染) | 否 | 是(配置后) |
| 用例 | 静态、共享内容(构建时) | 运行时上下文中的动态、共享内容(按请求) | 个性化、用户特定内容 |
注意: 虽然您不能在
'use cache: remote'内部调用await cookies()或await headers(),但可以在调用被'use cache: remote'包装的函数之前读取这些值,并且这些参数将包含在缓存键中。请注意,不建议这样做,因为它会显著增加缓存大小并降低缓存命中率。
根据您的用例选择正确的缓存指令:
何时使用 use cache:
何时使用 'use cache: remote':
await connection() 之后)何时使用 'use cache: private':
'use cache: remote' 指令通过将结果存储在服务器端缓存处理程序中,而不是在构建时进行预渲染,从而在动态上下文中实现共享数据的运行时缓存。
当 Next.js 遇到某些 API,例如 connection()、cookies() 或 headers() 时,上下文变为“动态”。在动态上下文中:
use cache 停止工作 - 它将不会缓存任何内容'use cache: remote' 继续工作 - 它由远程缓存处理程序缓存。值得注意的是: 如果没有
'use cache: remote',动态上下文中的函数将在每次请求时执行,这可能会造成性能瓶颈。远程缓存通过将结果存储在服务器端缓存处理程序中来消除此问题。
远程缓存使用服务器端缓存处理程序进行持久化,这些处理程序可能包括:
这意味着:
cacheTag 和 revalidateTag 工作cacheLife 配置控制async function UserDashboard() {
// Calling connection() makes the context dynamic
await connection()
// Without any caching directive, this runs on every request
const stats = await getStats()
// With 'use cache: remote', this is cached in the remote handler
const analytics = await getAnalytics()
return (
<div>
<Stats data={stats} />
<Analytics data={analytics} />
</div>
)
}
async function getAnalytics() {
'use cache: remote'
cacheLife({ expire: 300 }) // 5 minutes
// This expensive operation is cached and shared across all requests
return fetchAnalyticsData()
}虽然 'use cache: remote' 在技术上允许通过在调用被 'use cache: remote' 包装的函数之前调用 cookies() 和 headers() 等 API 来访问请求特定数据,但通常不建议将它们一起使用:
| API | 允许在 use cache 中使用 | 允许在 'use cache: remote' 中使用 | 推荐 |
|---|---|---|---|
cookies() | 否 | 否 | 请改用 'use cache: private' |
headers() | 否 | 否 | 请改用 'use cache: private' |
connection() | 否 | 否 | 否 - 这些永远无法被缓存 |
searchParams | 否 | 否 | 请改用 'use cache: private' |
重要: 如果您需要基于 cookies、headers 或 search params 进行缓存,请改用
'use cache: private'。远程缓存是跨所有用户共享的,因此在其中缓存用户特定数据可能会导致向不同用户提供不正确的结果。
远程缓存有特定的嵌套规则:
'use cache: remote')内部'use cache')内部'use cache: private')内部// VALID: Remote inside remote
async function outerRemote() {
'use cache: remote'
const result = await innerRemote()
return result
}
async function innerRemote() {
'use cache: remote'
return getData()
}
// VALID: Remote inside regular cache
async function outerCache() {
'use cache'
// If this is in a dynamic context, the inner remote cache will work
const result = await innerRemote()
return result
}
async function innerRemote() {
'use cache: remote'
return getData()
}
// INVALID: Remote inside private
async function outerPrivate() {
'use cache: private'
const result = await innerRemote() // Error!
return result
}
async function innerRemote() {
'use cache: remote'
return getData()
}
// INVALID: Private inside remote
async function outerRemote() {
'use cache: remote'
const result = await innerPrivate() // Error!
return result
}
async function innerPrivate() {
'use cache: private'
return getData()
}以下示例演示了使用 'use cache: remote' 的常见模式。有关 cacheLife 参数(stale、revalidate、expire)的详细信息,请参阅 cacheLife API 参考。
缓存动态上下文中访问的昂贵数据库查询,从而减少数据库负载:
import { connection } from 'next/server'
import { cacheLife, cacheTag } from 'next/cache'
export default async function DashboardPage() {
// Make context dynamic
await connection()
const stats = await getGlobalStats()
return <StatsDisplay stats={stats} />
}
async function getGlobalStats() {
'use cache: remote'
cacheTag('global-stats')
cacheLife({ expire: 60 }) // 1 minute
// This expensive database query is cached and shared across all users,
// reducing load on your database
const stats = await db.analytics.aggregate({
total_users: 'count',
active_sessions: 'count',
revenue: 'sum',
})
return stats
}缓存流式传输期间或动态操作后获取的 API 响应:
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cacheLife, cacheTag } from 'next/cache'
export default async function FeedPage() {
return (
<div>
<Suspense fallback={<Skeleton />}>
<FeedItems />
</Suspense>
</div>
)
}
async function FeedItems() {
// Dynamic context
await connection()
const items = await getFeedItems()
return items.map((item) => <FeedItem key={item.id} item={item} />)
}
async function getFeedItems() {
'use cache: remote'
cacheTag('feed-items')
cacheLife({ expire: 120 }) // 2 minutes
// This API call is cached, reducing requests to your external service
const response = await fetch('https://api.example.com/feed')
return response.json()
}缓存动态安全或功能检查后发生的昂贵计算:
import { connection } from 'next/server'
import { cacheLife } from 'next/cache'
export default async function ReportsPage() {
// Dynamic security check
await connection()
const report = await generateReport()
return <ReportViewer report={report} />
}
async function generateReport() {
'use cache: remote'
cacheLife({ expire: 3600 }) // 1 hour
// This expensive computation is cached and shared across all authorized users,
// avoiding repeated calculations
const data = await db.transactions.findMany()
return {
totalRevenue: calculateRevenue(data),
topProducts: analyzeProducts(data),
trends: calculateTrends(data),
}
}结合静态、远程和私有缓存以实现最佳性能:
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
// Static product data - prerendered at build time
async function getProduct(id: string) {
'use cache'
cacheTag(`product-${id}`)
// This is cached at build time and shared across all users
return db.products.find({ where: { id } })
}
// Shared pricing data - cached at runtime in remote handler
async function getProductPrice(id: string) {
'use cache: remote'
cacheTag(`product-price-${id}`)
cacheLife({ expire: 300 }) // 5 minutes
// This is cached at runtime and shared across all users
return db.products.getPrice({ where: { id } })
}
// User-specific recommendations - private cache per user
async function getRecommendations(productId: string) {
'use cache: private'
cacheLife({ expire: 60 }) // 1 minute
const sessionId = (await cookies()).get('session-id')?.value
// This is cached per-user and never shared
return db.recommendations.findMany({
where: { productId, sessionId },
})
}
export default async function ProductPage({ params }) {
const { id } = await params
// Static product data
const product = await getProduct(id)
return (
<div>
<ProductDetails product={product} />
{/* Dynamic shared price */}
<Suspense fallback={<PriceSkeleton />}>
<ProductPriceComponent productId={id} />
</Suspense>
{/* Dynamic personalized recommendations */}
<Suspense fallback={<RecommendationsSkeleton />}>
<ProductRecommendations productId={id} />
</Suspense>
</div>
)
}
function ProductDetails({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
)
}
async function ProductPriceComponent({ productId }) {
// Make this component dynamic
await connection()
const price = await getProductPrice(productId)
return <div>Price: ${price}</div>
}
async function ProductRecommendations({ productId }) {
const recommendations = await getRecommendations(productId)
return <RecommendationsList items={recommendations} />
}
function PriceSkeleton() {
return <div>Loading price...</div>
}
function RecommendationsSkeleton() {
return <div>Loading recommendations...</div>
}
function RecommendationsList({ items }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)
}值得注意的是:
- 远程缓存存储在服务器端缓存处理程序中,并跨所有用户共享
- 远程缓存适用于常规
use cache会失败的动态上下文- 使用
cacheTag()和revalidateTag()按需使远程缓存失效- 使用
cacheLife()配置缓存过期- 对于用户特定数据,请使用
'use cache: private'而不是'use cache: remote'- 远程缓存通过在服务器端存储计算或获取的数据来减少源站负载
| 部署选项 | 支持 |
|---|---|
Node.js server | 是 |
Docker container | 是 |
Static export | 否 |
Adapters | 是 |
| 版本 | 更改 |
|---|---|
v16.0.0 | "use cache: remote" 随缓存组件功能启用。 |