跳到内容
API 参考指令use cache: private

use cache: private

此功能目前在 Canary 通道中可用,并且可能会发生变化。通过升级 Next.js 来尝试此功能,并在 GitHub 上分享您的反馈。

'use cache: private' 指令支持运行时预取依赖于 cookies、请求头或搜索参数的个性化内容。

须知:'use cache: private'use cache 的一个变体,专门为需要预取但绝不应存储在服务器端缓存处理程序中的用户特定内容而设计。

用法

要使用 'use cache: private',请在您的 next.config.ts 文件中启用 cacheComponents 标志。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  cacheComponents: true,
}
 
export default nextConfig

然后将 'use cache: private' 添加到您的函数中,以及 cacheLife 配置,并从您的页面导出 unstable_prefetch

基本示例

app/product/[id]/page.tsx
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
// REQUIRED: Enable runtime prefetching
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    { params: { id: '1' }, cookies: [{ name: 'session-id', value: '1' }] },
  ],
}
 
export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
 
  return (
    <div>
      <ProductDetails id={id} />
      <Suspense fallback={<div>Loading recommendations...</div>}>
        <Recommendations productId={id} />
      </Suspense>
    </div>
  )
}
 
async function Recommendations({ productId }: { productId: string }) {
  const recommendations = await getRecommendations(productId)
 
  return (
    <div>
      {recommendations.map((rec) => (
        <ProductCard key={rec.id} product={rec} />
      ))}
    </div>
  )
}
 
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheTag(`recommendations-${productId}`)
  cacheLife({ stale: 60 }) // Minimum 30 seconds required for runtime prefetch
 
  // Access cookies within private cache functions
  const sessionId = (await cookies()).get('session-id')?.value || 'guest'
 
  return getPersonalizedRecommendations(productId, sessionId)
}

注意:私有缓存需要至少 30 秒的 cacheLife 过期时间才能启用运行时预取。低于 30 秒的值将被视为动态。

use cache 的区别

虽然常规的 use cache 旨在用于可以在服务器上缓存的静态、共享内容,但 'use cache: private' 专门用于动态、用户特定的内容,这些内容需要

  1. 个性化 - 根据 cookies、请求头或搜索参数而变化
  2. 可预取 - 可以在用户导航到页面之前加载
  3. 仅限客户端 - 绝不持久化到服务器端缓存处理程序
功能use cache'use cache: private'
访问 await cookies()
访问 await headers()
访问 await searchParams
存储在缓存处理程序中是(服务器端)否(仅限客户端)
运行时可预取不适用(已经是静态的)是(配置时)
缓存范围全局(共享)每个用户(隔离)
用例静态、共享内容个性化、用户特定内容

工作原理

运行时预取

当用户悬停或查看指向带有 unstable_prefetch = { mode: 'runtime' } 的页面的链接时

  1. 静态内容会立即预取(布局、页面外壳)
  2. 私有缓存函数会使用用户的当前 cookies/headers 执行
  3. 结果存储在客户端的恢复数据缓存中
  4. 导航是即时的 - 静态和个性化内容都已加载

须知:如果没有 'use cache: private',个性化内容无法预取,必须等到导航完成后才能加载。运行时预取通过使用用户的当前请求上下文执行缓存函数来消除此延迟。

存储行为

私有缓存绝不持久化到服务器端缓存处理程序(如 Redis、Vercel 数据缓存等)。它们仅用于

  1. 启用个性化内容的运行时预取
  2. 在会话期间将预取数据存储在客户端缓存中
  3. 通过标签和过期时间协调缓存失效

这确保了用户特定数据永远不会在用户之间意外共享,同时仍然支持快速、预取的导航。

过期时间要求

注意:cacheLife 过期时间少于 30 秒的函数即使使用 'use cache: private' 也不会在运行时预取。这可以防止预取变化快速的数据,这些数据在导航时可能已经过时。

// Will be runtime prefetched (stale ≥ 30s)
cacheLife({ stale: 60 })
 
// Will be runtime prefetched (stale ≥ 30s)
cacheLife({ stale: 30 })
 
// Will NOT be runtime prefetched (stale < 30s)
cacheLife({ stale: 10 })

允许在私有缓存中使用的请求 API

以下特定于请求的 API 可以在 'use cache: private' 函数中使用

API允许在 use cache 中使用允许在 'use cache: private' 中使用
cookies()
headers()
searchParams
connection()

注意:connection() API 在 use cache'use cache: private' 中都被禁止,因为它提供连接特定信息,无法安全缓存。

嵌套规则

私有缓存有特定的嵌套规则,以防止用户特定数据泄漏到共享缓存中

  • 私有缓存可以嵌套在其他私有缓存中
  • 私有缓存不能嵌套在公共缓存中('use cache', 'use cache: remote'
  • 公共缓存可以嵌套在私有缓存中
// VALID: Private inside private
async function outerPrivate() {
  'use cache: private'
  const result = await innerPrivate()
  return result
}
 
async function innerPrivate() {
  'use cache: private'
  return getData()
}
 
// INVALID: Private inside public
async function outerPublic() {
  'use cache'
  const result = await innerPrivate() // Error!
  return result
}
 
async function innerPrivate() {
  'use cache: private'
  return getData()
}

示例

个性化产品推荐

此示例展示了如何根据用户的会话 cookie 缓存个性化产品推荐。当用户悬停在产品链接上时,推荐会在运行时预取。

app/product/[id]/page.tsx
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    { params: { id: '1' }, cookies: [{ name: 'user-id', value: 'user-123' }] },
  ],
}
 
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheTag(`recommendations-${productId}`)
  cacheLife({ stale: 60 })
 
  const userId = (await cookies()).get('user-id')?.value
 
  // Fetch personalized recommendations based on user's browsing history
  const recommendations = await db.recommendations.findMany({
    where: { userId, productId },
  })
 
  return recommendations
}

用户特定定价

缓存根据用户级别而变化的定价信息,允许即时导航到定价页面,并已加载个性化费率。

app/pricing/page.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [{ cookies: [{ name: 'user-tier', value: 'premium' }] }],
}
 
async function getPricing() {
  'use cache: private'
  cacheLife({ stale: 300 }) // 5 minutes
 
  const tier = (await cookies()).get('user-tier')?.value || 'free'
 
  // Return tier-specific pricing
  return db.pricing.findMany({ where: { tier } })
}

基于请求头的本地化内容

根据用户的 Accept-Language 请求头提供本地化内容,在导航前预取正确的语言变体。

app/page.tsx
import { headers } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [{ headers: [{ name: 'accept-language', value: 'en-US' }] }],
}
 
async function getLocalizedContent() {
  'use cache: private'
  cacheTag('content')
  cacheLife({ stale: 3600 }) // 1 hour
 
  const headersList = await headers()
  const locale = headersList.get('accept-language')?.split(',')[0] || 'en-US'
 
  return db.content.findMany({ where: { locale } })
}

包含用户偏好的搜索结果

预取包含用户特定偏好的搜索结果,确保个性化搜索结果即时加载。

app/search/page.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    {
      searchParams: { q: 'laptop' },
      cookies: [{ name: 'preferences', value: 'compact-view' }],
    },
  ],
}
 
async function getSearchResults(query: string) {
  'use cache: private'
  cacheLife({ stale: 120 }) // 2 minutes
 
  const preferences = (await cookies()).get('preferences')?.value
 
  // Apply user preferences to search results
  return searchWithPreferences(query, preferences)
}

须知:

  • 私有缓存是短暂的,仅在客户端缓存中存在于会话期间。
  • 私有缓存结果从不写入服务器端缓存处理程序。
  • unstable_prefetch 导出是运行时预取工作的必要条件。
  • 私有缓存的预取需要至少 30 秒的过期时间。
  • 您可以使用 cacheTag()revalidateTag() 来使私有缓存失效。
  • 每个用户根据其 cookies/headers 获取自己的私有缓存条目。

平台支持

版本历史

版本更改
v16.0.0'use cache: private' 作为实验性功能引入。