主题
使用缓存
¥use cache
use cache 指令允许你将路由、React 组件或函数标记为可缓存。它可以用在文件顶部,以指示文件中的所有导出都应该被缓存,或者内联在函数或组件的顶部以缓存返回值。
¥The use cache directive allows you to mark a route, React component, or a function as cacheable. It can be used at the top of a file to indicate that all exports in the file should be cached, or inline at the top of function or component to cache the return value.
需要了解:
¥Good to know:
要使用 cookie 或标头,请在缓存范围之外读取它们,并将值作为参数传递。这是首选模式。
¥To use cookies or headers, read them outside cached scopes and pass values as arguments. This is the preferred pattern.
如果内存缓存不足以存储运行时数据,
'use cache: remote'允许平台提供专用的缓存处理器,但这需要一次网络往返来检查缓存,并且通常会产生平台费用。¥If the in-memory cache isn't sufficient for runtime data,
'use cache: remote'allows platforms to provide a dedicated cache handler, though it requires a network roundtrip to check the cache and typically incurs platform fees.对于合规性要求或无法重构以将运行时数据作为参数传递给
use cache作用域的情况,请参阅'use cache: private'。¥For compliance requirements or when you can't refactor to pass runtime data as arguments to a
use cachescope, see'use cache: private'.
用法
¥Usage
use cache 是缓存组件的一项功能。要启用此功能,请在 next.config.ts 文件中添加 cacheComponents 选项:
¥use cache is a Cache Components feature. To enable it, add the cacheComponents option to your next.config.ts file:
ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig然后,在文件、组件或函数级别添加 use cache:
¥Then, add use cache at the file, component, or function level:
tsx
// File level
'use cache'
export default async function Page() {
// ...
}
// Component level
export async function MyComponent() {
'use cache'
return <></>
}
// Function level
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}需要了解:在文件级别使用时,所有函数导出都必须是异步函数。
¥Good to know: When used at file level, all function exports must be async functions.
use cache 的工作原理
¥How use cache works
缓存键
¥Cache keys
缓存条目的键是使用其输入的序列化版本生成的,其中包括:
¥A cache entry's key is generated using a serialized version of its inputs, which includes:
构建 ID - 每个构建都唯一,更改此标识符会使所有缓存条目失效
¥Build ID - Unique per build, changing this invalidates all cache entries
函数 ID - 代码库中函数位置和签名的安全哈希值。
¥Function ID - A secure hash of the function's location and signature in the codebase
可序列化的参数 - 组件的属性或函数参数
¥Serializable arguments - Props (for components) or function arguments
HMR 刷新哈希(仅限开发) - 在热模块替换时使缓存失效
¥HMR refresh hash (development only) - Invalidates cache on hot module replacement
当缓存函数引用外部作用域的变量时,这些变量会被自动捕获并绑定为参数,使其成为缓存键的一部分。
¥When a cached function references variables from outer scopes, those variables are automatically captured and bound as arguments, making them part of the cache key.
tsx
async function Component({ userId }: { userId: string }) {
const getData = async (filter: string) => {
'use cache'
// Cache key includes both userId (from closure) and filter (argument)
return fetch(`/api/users/${userId}/data?filter=${filter}`)
}
return getData('active')
}在上面的代码片段中,userId 是从外部作用域捕获的,而 filter 作为参数传递,因此两者都成为 getData 函数缓存键的一部分。这意味着不同的用户和过滤器组合将拥有不同的缓存条目。
¥In the snippet above, userId is captured from the outer scope and filter is passed as an argument, so both become part of the getData function's cache key. This means different user and filter combinations will have separate cache entries.
序列化
¥Serialization
缓存函数的参数及其返回值必须是可序列化的。
¥Arguments to cached functions and their return values must be serializable.
有关完整参考,请参阅:
¥For a complete reference, see:
可序列化的参数 - 使用 React 服务器组件序列化
¥Serializable arguments - Uses React Server Components serialization
可序列化的返回类型 - 使用 React 客户端组件序列化
¥Serializable return types - Uses React Client Components serialization
需要了解:参数和返回值使用不同的序列化系统。服务器组件的序列化(针对参数)比客户端组件的序列化(针对返回值)更严格。这意味着你可以返回 JSX 元素,但除非使用传递模式,否则不能将它们作为参数接受。
¥Good to know: Arguments and return values use different serialization systems. Server Component serialization (for arguments) is more restrictive than Client Component serialization (for return values). This means you can return JSX elements but cannot accept them as arguments unless using pass-through patterns.
支持的类型
¥Supported types
参数:
¥Arguments:
基本功能:
string、number、boolean、null、undefined¥Primitives:
string,number,boolean,null,undefined普通对象:
{ key: value }¥Plain objects:
{ key: value }数组:
[1, 2, 3]¥Arrays:
[1, 2, 3]日期、地图、集合、类型化数组、数组缓冲区
¥Dates, Maps, Sets, TypedArrays, ArrayBuffers
React 元素(仅作为传递)
¥React elements (as pass-through only)
返回值:
¥Return values:
与参数相同,外加 JSX 元素
¥Same as arguments, plus JSX elements
不支持的类型
¥Unsupported types
类实例
¥Class instances
函数(传递函数除外)
¥Functions (except as pass-through)
符号、弱映射、弱集
¥Symbols, WeakMaps, WeakSets
URL 实例
¥URL instances
tsx
// Valid - primitives and plain objects
async function UserCard({
id,
config,
}: {
id: string
config: { theme: string }
}) {
'use cache'
return <div>{id}</div>
}
// Invalid - class instance
async function UserProfile({ user }: { user: UserClass }) {
'use cache'
// Error: Cannot serialize class instance
return <div>{user.name}</div>
}传递(不可序列化的参数)
¥Pass-through (non-serializable arguments)
只要不进行内省,你可以接受不可序列化的值。这支持使用 children 和服务器操作的组合模式:
¥You can accept non-serializable values as long as you don't introspect them. This enables composition patterns with children and Server Actions:
tsx
async function CachedWrapper({ children }: { children: ReactNode }) {
'use cache'
// Don't read or modify children - just pass it through
return (
<div className="wrapper">
<header>Cached Header</header>
{children}
</div>
)
}
// Usage: children can be dynamic
export default function Page() {
return (
<CachedWrapper>
<DynamicComponent /> {/* Not cached, passed through */}
</CachedWrapper>
)
}你还可以通过缓存组件传递服务器操作:
¥You can also pass Server Actions through cached components:
tsx
async function CachedForm({ action }: { action: () => Promise<void> }) {
'use cache'
// Don't call action here - just pass it through
return <form action={action}>{/* ... */}</form>
}约束
¥Constraints
缓存的函数和组件不能直接访问运行时 API,例如 cookies()、headers() 或 searchParams。而是从缓存范围之外读取这些值,并将其作为参数传递。
¥Cached functions and components cannot directly access runtime APIs like cookies(), headers(), or searchParams. Instead, read these values outside the cached scope and pass them as arguments.
运行时缓存注意事项
¥Runtime caching considerations
虽然 use cache 的主要设计目的是在静态 shell 中包含动态数据,但它也可以使用内存中的 LRU(最近最少使用)存储来缓存运行时数据。
¥While use cache is designed primarily to include dynamic data in the static shell, it can also cache data at runtime using in-memory LRU (Least Recently Used) storage.
运行时缓存行为取决于你的托管环境:
¥Runtime cache behavior depends on your hosting environment:
| 环境 | 运行时缓存行为 |
|---|---|
| 无服务器架构 | 缓存条目通常不会在请求之间保持不变(每个请求都可以是不同的实例)。构建时缓存正常工作。 |
| 自托管 | 缓存条目在请求之间保持不变。使用 cacheMaxMemorySize 控制缓存大小。 |
如果默认的内存缓存不足,请考虑使用 use cache: remote,它允许平台提供专用的缓存处理器(例如 Redis 或 KV 数据库)。这有助于减少对未扩展到总流量的数据源的访问,但会带来一些成本(存储、网络延迟、平台费用)。
¥If the default in-memory cache isn't enough, consider use cache: remote which allows platforms to provide a dedicated cache handler (like Redis or KV database). This helps reduce hits against data sources not scaled to your total traffic, though it comes with costs (storage, network latency, platform fees).
极少数情况下,出于合规性要求或无法重构代码以将运行时数据作为参数传递给 use cache 作用域时,你可能需要使用 use cache: private。
¥Very rarely, for compliance requirements or when you can't refactor your code to pass runtime data as arguments to a use cache scope, you might need use cache: private.
运行时 use cache
¥use cache at runtime
在服务器端,缓存条目存储在内存中,并遵循 cacheLife 配置中的 revalidate 和 expire 时间。你可以通过在 next.config.js 文件中配置 cacheHandlers 来自定义缓存存储。
¥On the server, cache entries are stored in-memory and respect the revalidate and expire times from your cacheLife configuration. You can customize the cache storage by configuring cacheHandlers in your next.config.js file.
在客户端,服务器缓存中的内容会存储在浏览器内存中,存储时间由 stale 设定。客户端路由强制执行至少 30 秒的过期时间,无论配置如何。
¥On the client, content from the server cache is stored in the browser's memory for the duration defined by the stale time. The client router enforces a minimum 30-second stale time, regardless of configuration.
x-nextjs-stale-time 响应标头将缓存生命周期从服务器传递到客户端,确保行为协调。
¥The x-nextjs-stale-time response header communicates cache lifetime from server to client, ensuring coordinated behavior.
重新验证
¥Revalidation
默认情况下,use cache 使用 default 配置文件,并采用以下设置:
¥By default, use cache uses the default profile with these settings:
stale:5 分钟(客户端)
¥stale: 5 minutes (client-side)
revalidate:15 分钟(服务器端)
¥revalidate: 15 minutes (server-side)
expire:永不过期
¥expire: Never expires by time
tsx
async function getData() {
'use cache'
// Implicitly uses default profile
return fetch('/api/data')
}自定义缓存生命周期
¥Customizing cache lifetime
使用 cacheLife 函数自定义缓存持续时间:
¥Use the cacheLife function to customize cache duration:
tsx
import { cacheLife } from 'next/cache'
async function getData() {
'use cache'
cacheLife('hours') // Use built-in 'hours' profile
return fetch('/api/data')
}按需重新验证
¥On-demand revalidation
使用 cacheTag、updateTag 或 revalidateTag 实现按需缓存失效:
¥Use cacheTag, updateTag, or revalidateTag for on-demand cache invalidation:
tsx
import { cacheTag } from 'next/cache'
async function getProducts() {
'use cache'
cacheTag('products')
return fetch('/api/products')
}tsx
'use server'
import { updateTag } from 'next/cache'
export async function updateProduct() {
await db.products.update(...)
updateTag('products') // Invalidates all 'products' caches
}cacheLife 和 cacheTag 都集成了客户端和服务端缓存层,这意味着你只需在一个地方配置缓存语义,即可在所有地方应用。
¥Both cacheLife and cacheTag integrate across client and server caching layers, meaning you configure your caching semantics in one place and they apply everywhere.
示例
¥Examples
使用 use cache 缓存整个路由
¥Caching an entire route with use cache
要预渲染整个路由,请在 layout 和 page 文件的顶部都添加 use cache。这些段中的每一个都被视为应用中的单独入口点,并将被独立缓存。
¥To pre-render an entire route, add use cache to the top of both the layout and page files. Each of these segments are treated as separate entry points in your application, and will be cached independently.
tsx
'use cache'
export default async function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}任何导入并嵌套在 page 文件中的组件都是与 page 关联的缓存输出的一部分。
¥Any components imported and nested in page file are part of the cache output associated with the page.
tsx
'use cache'
async function Users() {
const users = await fetch('/api/users')
// loop through users
}
export default async function Page() {
return (
<main>
<Users />
</main>
)
}需要了解:
¥Good to know:
如果仅将
use cache添加到layout或page,则只有该路由段及其导入的任何组件才会被缓存。¥If
use cacheis added only to thelayoutor thepage, only that route segment and any components imported into it will be cached.
使用 use cache 缓存组件输出
¥Caching a component's output with use cache
你可以在组件级别使用 use cache 来缓存在该组件内执行的任何提取或计算。只要序列化的 props 在每个实例中产生相同的值,缓存条目就会被重用。
¥You can use use cache at the component level to cache any fetches or computations performed within that component. The cache entry will be reused as long as the serialized props produce the same value in each instance.
tsx
export async function Bookings({ type = 'haircut' }: BookingsProps) {
'use cache'
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
interface BookingsProps {
type: string
}使用 use cache 缓存函数输出
¥Caching function output with use cache
由于你可以将 use cache 添加到任何异步函数,因此你不仅限于缓存组件或路由。你可能需要缓存网络请求、数据库查询或慢速计算。
¥Since you can add use cache to any asynchronous function, you aren't limited to caching components or routes only. You might want to cache a network request, a database query, or a slow computation.
tsx
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}交错
¥Interleaving
在 React 中,使用 children 或插槽进行组合是构建灵活组件的常用模式。使用 use cache 时,你可以继续以这种方式构建 UI。返回的 JSX 中包含的任何 children 或其他组合插槽的内容都将通过缓存组件传递,而不会影响其缓存条目。
¥In React, composition with children or slots is a well-known pattern for building flexible components. When using use cache, you can continue to compose your UI in this way. Anything included as children, or other compositional slots, in the returned JSX will be passed through the cached component without affecting its cache entry.
只要你不在可缓存函数的主体中直接引用任何 JSX 插槽,它们出现在返回的输出中就不会影响缓存条目。
¥As long as you don't directly reference any of the JSX slots inside the body of the cacheable function itself, their presence in the returned output won't affect the cache entry.
tsx
export default async function Page() {
const uncachedData = await getData()
return (
// Pass compositional slots as props, e.g. header and children
<CacheComponent header={<h1>Home</h1>}>
{/* DynamicComponent is provided as the children slot */}
<DynamicComponent data={uncachedData} />
</CacheComponent>
)
}
async function CacheComponent({
header, // header: a compositional slot, injected as a prop
children, // children: another slot for nested composition
}: {
header: ReactNode
children: ReactNode
}) {
'use cache'
const cachedData = await fetch('/api/cached-data')
return (
<div>
{header}
<PrerenderedComponent data={cachedData} />
{children}
</div>
)
}你也可以通过缓存的组件将服务器操作传递给客户端组件,而无需在可缓存函数内调用它们。
¥You can also pass Server Actions through cached components to Client Components without invoking them inside the cacheable function.
tsx
import ClientComponent from './ClientComponent'
export default async function Page() {
const performUpdate = async () => {
'use server'
// Perform some server-side update
await db.update(...)
}
return <CachedComponent performUpdate={performUpdate} />
}
async function CachedComponent({
performUpdate,
}: {
performUpdate: () => Promise<void>
}) {
'use cache'
// Do not call performUpdate here
return <ClientComponent action={performUpdate} />
}tsx
'use client'
export default function ClientComponent({
action,
}: {
action: () => Promise<void>
}) {
return <button onClick={action}>Update</button>
}故障排除
¥Troubleshooting
调试缓存行为
¥Debugging cache behavior
详细日志记录
¥Verbose logging
设置 NEXT_PRIVATE_DEBUG_CACHE=1 可启用详细缓存日志记录:
¥Set NEXT_PRIVATE_DEBUG_CACHE=1 for verbose cache logging:
bash
NEXT_PRIVATE_DEBUG_CACHE=1 npm run dev
# or for production
NEXT_PRIVATE_DEBUG_CACHE=1 npm run start需要了解:此环境变量还会记录中断服务例程 (ISR) 和其他缓存机制。详细信息请参见 验证正确的生产行为。
¥Good to know: This environment variable also logs ISR and other caching mechanisms. See Verifying correct production behavior for more details.
控制台日志重放
¥Console log replays
在开发环境中,缓存函数的控制台日志会以 Cache 为前缀。
¥In development, console logs from cached functions appear with a Cache prefix.
构建挂起(缓存超时)
¥Build Hangs (Cache Timeout)
如果你的构建挂起,则说明你正在访问解析为动态或运行时数据的 Promise,这些数据是在 use cache 边界之外创建的。缓存函数会等待构建过程中无法解析的数据,导致 50 秒后超时。
¥If your build hangs, you're accessing Promises that resolve to dynamic or runtime data, created outside a use cache boundary. The cached function waits for data that can't resolve during the build, causing a timeout after 50 seconds.
构建超时时,你将看到以下错误消息:
¥When the build timeouts you'll see this error message:
错误:预渲染期间填充缓存超时,可能是因为在 "使用缓存" 中使用了请求特定的参数,例如 params、searchParams、cookies() 或动态数据。
¥Error: Filling a cache during prerender timed out, likely because request-specific arguments such as params, searchParams, cookies() or dynamic data were used inside "use cache".
常见实现方式:可以将此类 Promise 作为 props 传递、通过闭包访问或从共享存储(Maps)中检索。
¥Common ways this happens: passing such Promises as props, accessing them via closure, or retrieving them from shared storage (Maps).
需要了解:在
use cache中直接调用cookies()或headers()会立即失败,并返回 不同的错误 错误,而不是超时。¥Good to know: Directly calling
cookies()orheaders()insideuse cachefails immediately with a different error, not a timeout.
将运行时数据 Promise 作为 props 传递:
¥Passing runtime data Promises as props:
tsx
import { cookies } from 'next/headers'
import { Suspense } from 'react'
export default function Page() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Dynamic />
</Suspense>
)
}
async function Dynamic() {
const cookieStore = cookies()
return <Cached promise={cookieStore} /> // Build hangs
}
async function Cached({ promise }: { promise: Promise<unknown> }) {
'use cache'
const data = await promise // Waits for runtime data during build
return <p>..</p>
}在 Dynamic 组件中等待 cookies 存储,并将 cookie 值传递给 Cached 组件。
¥Await the cookies store in the Dynamic component, and pass a cookie value to the Cached component.
共享去重存储:
¥Shared deduplication storage:
tsx
// Problem: Map stores dynamic Promises, accessed by cached code
import { Suspense } from 'react'
const cache = new Map<string, Promise<string>>()
export default function Page() {
return (
<>
<Suspense fallback={<div>Loading...</div>}>
<Dynamic id="data" />
</Suspense>
<Cached id="data" />
</>
)
}
async function Dynamic({ id }: { id: string }) {
// Stores dynamic Promise in shared Map
cache.set(
id,
fetch(`https://api.example.com/${id}`).then((r) => r.text())
)
return <p>Dynamic</p>
}
async function Cached({ id }: { id: string }) {
'use cache'
return <p>{await cache.get(id)}</p> // Build hangs - retrieves dynamic Promise
}使用 Next.js 内置的 fetch() 去重功能,或为缓存和非缓存上下文使用单独的 Map。
¥Use Next.js's built-in fetch() deduplication or use separate Maps for cached and uncached contexts.
平台支持
¥Platform Support
| 部署选项 | 支持 |
|---|---|
| Node.js 服务器 | 是 |
| Docker 容器 | 是 |
| 静态导出 | 否 |
| 适配器 | 平台相关 |
了解如何在自托管 Next.js 时进行 配置缓存。
¥Learn how to configure caching when self-hosting Next.js.
版本历史
¥Version History
| 版本 | 更改 |
|---|---|
v16.0.0 | "use cache" 通过缓存组件功能启用。 |
v15.0.0 | "use cache" 作为一项实验性功能推出。 |