跳到内容

use cache

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

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

用法

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

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

此外,当设置 dynamicIO 标志时,也会启用 use cache 指令。

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

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

须知

  • use cache 是一个实验性的 Next.js 功能,而不是像 use clientuse server 这样的原生 React 功能。
  • 传递给缓存函数的任何可序列化的参数(或 props),以及它从父作用域读取的任何可序列化值,都将转换为类似 JSON 的格式,并自动成为缓存键的一部分。
  • 任何不可序列化的参数、props 或闭包值都将变成缓存函数内部的不透明引用,并且只能传递,而不能检查或修改。这些不可序列化的值将在请求时填充,并且不会成为缓存键的一部分。
    • 例如,缓存函数可以将 JSX 作为 children prop 接收,并返回 <div>{children}</div>,但它无法内省实际的 children 对象。
  • 可缓存函数的返回值也必须是可序列化的。这确保缓存的数据可以正确存储和检索。
  • 使用 use cache 指令的函数不得有任何副作用,例如修改状态、直接操作 DOM 或设置定时器以按时间间隔执行代码。
  • 如果与 部分预渲染 一起使用,具有 use cache 的段将作为静态 HTML shell 的一部分进行预渲染。
  • 与仅支持 JSON 数据的 unstable_cache 不同,use cache 可以缓存 React 可以渲染的任何可序列化数据,包括组件的渲染输出。

示例

使用 use cache 缓存整个路由

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

app/layout.tsx
'use cache'
 
export default function Layout({ children }: { children: ReactNode }) {
  return <div>{children}</div>
}

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

app/page.tsx
'use cache'
 
async function Users() {
  const users = await fetch('/api/users')
  // loop through users
}
 
export default function Page() {
  return (
    <main>
      <Users />
    </main>
  )
}

建议以前使用 export const dynamic = "force-static" 选项的应用程序使用此方法,这将确保整个路由被预渲染。

使用 use cache 缓存组件输出

你可以在组件级别使用 use cache 来缓存在该组件内执行的任何获取或计算。当你在整个应用程序中重用该组件时,只要 props 保持相同的结构,它就可以共享相同的缓存条目。

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

app/components/bookings.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 缓存函数输出

由于你可以将 use cache 添加到任何异步函数,因此你不限于仅缓存组件或路由。你可能想要缓存网络请求或数据库查询,或者计算一些非常慢的事情。通过将 use cache 添加到包含此类工作的函数,它将变为可缓存的,并且在重用时将共享相同的缓存条目。

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

重新验证

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

虽然此重新验证周期可能对您不希望经常更改的内容很有用,但您可以使用 cacheLifecacheTag API 来配置缓存行为

  • cacheLife:用于基于时间的重新验证周期。
  • cacheTag:用于标记缓存数据。

这两个 API 都跨客户端和服务器缓存层集成,这意味着您可以在一个地方配置缓存语义,并使其应用于所有地方。

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

使失效

要使缓存的数据失效,您可以使用 revalidateTag 函数。

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

交错

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

app/page.tsx
export default async function Page() {
  const uncachedData = await getData()
  return (
    <CacheComponent>
      <DynamicComponent data={uncachedData} />
    </CacheComponent>
  )
}
 
async function CacheComponent({ children }: { children: ReactNode }) {
  'use cache'
  const cachedData = await fetch('/api/cached-data')
  return (
    <div>
      <PrerenderedComponent data={cachedData} />
      {children}
    </div>
  )
}

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

app/page.tsx
import ClientComponent from './ClientComponent'
 
export default async function Page() {
  const performUpdate = async () => {
    'use server'
    // Perform some server-side update
    await db.update(...)
  }
 
  return <CacheComponent performUpdate={performUpdate} />
}
 
async function CachedComponent({
  performUpdate,
}: {
  performUpdate: () => Promise<void>
}) {
  'use cache'
  // Do not call performUpdate here
  return <ClientComponent action={performUpdate} />
}
app/ClientComponent.tsx
'use client'
 
export default function ClientComponent({
  action,
}: {
  action: () => Promise<void>
}) {
  return <button onClick={action}>Update</button>
}