Skip to main content

使用缓存

use cache 指令指定要缓存的组件和/或函数。它可用于文件顶部,以指示文件中的所有导出都是可缓存的,或内联在函数或组件顶部,以通知 Next.js 返回值应被缓存并重新用于后续请求。这是一个实验性的 Next.js 功能,而不是像 use clientuse server 这样的原生 React 功能。

¥The use cache directive designates a component and/or a function to be cached. It can be used at the top of a file to indicate that all exports in the file are cacheable, or inline at the top of a function or component to inform Next.js the return value should be cached and reused for subsequent requests. This is an experimental Next.js feature, and not a native React feature like use client or use server.

用法

¥Usage

next.config.ts 文件中使用 dynamicIO 标志启用对 use cache 指令的支持:

¥Enable support for the use cache directive with the dynamicIO flag in your next.config.ts file:

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
}

export default nextConfig

然后,你可以在文件、组件或函数级别使用 use cache 指令:

¥Then, you can use the use cache directive at the file, component, or function level:

// 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

  • use cache 是一个实验性的 Next.js 功能,而不是像 use clientuse server 这样的原生 React 功能。

    ¥use cache is an experimental Next.js feature, and not a native React feature like use client or use server.

  • 传递给缓存函数的任何 serializable 参数(或 props),以及它从父范围读取的任何可序列化值,都将转换为 JSON 等格式,并自动成为缓存键的一部分。

    ¥Any serializable arguments (or props) passed to the cached function, as well as any serializable values it reads from the parent scope, will be converted to a format like JSON and automatically become a part of the cache key.

  • 任何不可序列化的参数、props 或封闭值都将在缓存函数内变成不透明引用,只能传递而不能检查或修改。这些不可序列化的值将在请求时填写,不会成为缓存键的一部分。

    ¥Any non-serializable arguments, props, or closed-over values will turn into opaque references inside the cached function, and can be only passed through and not inspected nor modified. These non-serializable values will be filled in at the request time and won't become a part of the cache key.

    • 例如,缓存函数可以将 JSX 作为 children prop 接收并返回 <div>{children}</div>,但它无法自省实际的 children 对象。

      ¥For example, a cached function can take in JSX as a children prop and return <div>{children}</div>, but it won't be able to introspect the actual children object.

  • 可缓存函数的返回值也必须是可序列化的。这可确保缓存的数据可以正确存储和检索。

    ¥The return value of the cacheable function must also be serializable. This ensures that the cached data can be stored and retrieved correctly.

  • 使用 use cache 指令的函数不得有任何副作用,例如修改状态、直接操作 DOM 或设置计时器以间隔执行代码。

    ¥Functions that use the use cache directive must not have any side-effects, such as modifying state, directly manipulating the DOM, or setting timers to execute code at intervals.

  • 如果与 部分预渲染 一起使用,具有 use cache 的段将作为静态 HTML shell 的一部分进行预渲染。

    ¥If used alongside Partial Prerendering, segments that have use cache will be prerendered as part of the static HTML shell.

  • use cache 指令将来将与 dynamicIO 标志分开提供。

    ¥The use cache directive will be available separately from the dynamicIO flag in the future.

  • 与仅支持 JSON 数据的 unstable_cache 不同,use cache 可以缓存 React 可以渲染的任何可序列化数据,包括组件的渲染输出。

    ¥Unlike unstable_cache which only supports JSON data, use cache can cache any serializable data React can render, including the render output of components.

示例

¥Examples

使用 use cache 缓存整个路由

¥Caching entire routes with use cache

要预渲染整个路由,请将 use cache 添加到 layoutpage 文件的顶部。这些段中的每一个都被视为应用中的单独入口点,并将被独立缓存。

¥To prerender an entire route, add use cache to the top both the layout and page files. Each of these segments are treated as separate entry points in your application, and will be cached independently.

"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'

export default Layout({children}: {children: ReactNode}) {
return <div>{children}</div>
}
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'

export default Layout({ children }) {
return <div>{children}</div>
}

导入并嵌套在 page 文件中的任何组件都将继承 page 的缓存行为。

¥Any components imported and nested in page file will inherit the cache behavior of page.

"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'

async function Users() {
const users = await fetch('/api/users');
// loop through users
}

export default Page() {
return (
<main>
<Users/>
</main>
)
}
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'

async function Users() {
const users = await fetch('/api/users');
// loop through users
}

export default Page() {
return (
<main>
<Users/>
</main>
)
}

建议以前使用 export const dynamic = "force-static" 选项的应用使用此功能,并确保整个路由已预渲染。

¥This is recommended for applications that previously used the export const dynamic = "force-static" option, and will ensure the entire route is prerendered.

使用 use cache 缓存组件输出

¥Caching component 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. When you reuse the component throughout your application it can share the same cache entry as long as the props maintain the same structure.

props 被序列化并构成缓存键的一部分,只要序列化的 props 在每个实例中产生相同的值,缓存条目就会被重用。

¥The props are serialized and form part of the cache key, and the cache entry will be reused as long as the serialized props produce the same value in each instance.

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
}
export async function Bookings({ type = 'haircut' }) {
'use cache'
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}

使用 use cache 缓存函数输出

¥Caching function output with use cache

由于你可以将 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 or database query or compute something that is very slow. By adding use cache to a function containing this type of work it becomes cacheable, and when reused, will share the same cache entry.

export async function getData() {
'use cache'

const data = await fetch('/api/data')
return data
}
export async function getData() {
'use cache'

const data = await fetch('/api/data')
return data
}

重新验证

¥Revalidating

默认情况下,当你使用 use cache 指令时,Next.js 会将 重新验证期 设置为 15 分钟。Next.js 设置了近乎无限的过期时间,这意味着它适用于不需要频繁更新的内容。

¥By default, Next.js sets a revalidation period of 15 minutes when you use the use cache directive. Next.js sets a near-infinite expiration duration, meaning it's suitable for content that doesn't need frequent updates.

虽然这个重新验证期可能对你不经常更改的内容有用,但你可以使用 cacheLifecacheTag API 来配置缓存行为:

¥While this revalidation period may be useful for content you don't expect to change often, you can use the cacheLife and cacheTag APIs to configure the cache behavior:

  • cacheLife:用于基于时间的重新验证期。

    ¥cacheLife: For time-based revalidation periods.

  • cacheTag:用于按需重新验证。

    ¥cacheTag: For on-demand revalidation.

这两个 API 都集成在客户端和服务器缓存层中,这意味着你可以在一个地方配置缓存语义并让它们应用于任何地方。

¥Both of these APIs integrate across the client and server caching layers, meaning you can configure your caching semantics in one place and have them apply everywhere.

有关更多信息,请参阅 cacheLifecacheTag 文档。

¥See the cacheLife and cacheTag docs for more information.

交错

¥Interleaving

如果你需要将不可序列化的参数传递给可缓存函数,则可以将它们作为 children 传递。这意味着 children 引用可以更改而不会影响缓存条目。

¥If you need to pass non-serializable arguments to a cacheable function, you can pass them as children. This means the children reference can change without affecting the cache entry.

async function Page() {
const uncachedData = await getData()
return (
<CacheComponent>
<DynamicComponent data={uncachedData} />
</CacheComponent>
)
}

async function CacheComponent({ children }) {
'use cache'
const cachedData = await fetch('/api/cached-data')
return (
<div>
<PrerenderedComponent data={cachedData} />
{children}
</div>
)
}

你也可以通过缓存的组件将服务器操作传递给客户端组件,而无需在可缓存函数内调用它们。

¥You can also pass Server Actions through cached components to Client Components without invoking them inside the cacheable function.

import ClientComponent from './ClientComponent';

async function Page() {
const performUpdate = async () => {
"use server"
// Perform some server-side update
await db.update(...);
};

return <CacheComponent performUpdate={performUpdate} />;
}

async function CachedComponent({ performUpdate }) {
"use cache"
// Do not call performUpdate here
return <ClientComponent action={performUpdate} />;
}
import ClientComponent from './ClientComponent';

async function Page() {
const performUpdate = async () => {
"use server"
// Perform some server-side update
await db.update(...);
};

return <CacheComponent performUpdate={performUpdate} />;
}

async function CachedComponent({ performUpdate }) {
"use cache"
// Do not call performUpdate here
return <ClientComponent action={performUpdate} />;
}
'use client'

export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}
'use client'

export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}