Skip to main content

Next.js 中的缓存

Next.js 通过缓存渲染工作和数据请求来提高应用的性能并降低成本。本页面深入介绍了 Next.js 缓存机制、可用于配置它们的 API 以及它们如何相互交互。

¥Next.js improves your application's performance and reduces costs by caching rendering work and data requests. This page provides an in-depth look at Next.js caching mechanisms, the APIs you can use to configure them, and how they interact with each other.

很高兴知道:本页面可帮助你了解 Next.js 的底层工作原理,但并不是高效使用 Next.js 的必备知识。大多数 Next.js 的缓存启发式方法由你的 API 使用情况决定,并且具有默认值,可通过零或最少的配置获得最佳性能。

¥Good to know: This page helps you understand how Next.js works under the hood but is not essential knowledge to be productive with Next.js. Most of Next.js' caching heuristics are determined by your API usage and have defaults for the best performance with zero or minimal configuration.

概述

¥Overview

以下是不同缓存机制及其用途的高级概述:

¥Here's a high-level overview of the different caching mechanisms and their purpose:

机制什么在哪里目的期间
请求记忆函数的返回值服务器重用 React 组件树中的数据每个请求的生命周期
数据缓存数据服务器跨用户请求和部署存储数据持久(可以重新验证)
全路由缓存HTML 和 RSC 有效负载服务器降低渲染成本并提高性能持久(可以重新验证)
路由缓存RSC 有效负载客户减少导航上的服务器请求用户会话或基于时间

默认情况下,Next.js 会尽可能多地缓存以提高性能并降低成本。这意味着除非你选择退出,否则路由将静态渲染并且数据请求将被缓存。下图显示了默认的缓存行为:当在构建时静态渲染路由时以及首次访问静态路由时。

¥By default, Next.js will cache as much as possible to improve performance and reduce cost. This means routes are statically rendered and data requests are cached unless you opt out. The diagram below shows the default caching behavior: when a route is statically rendered at build time and when a static route is first visited.

缓存行为会发生变化,具体取决于路由是静态还是动态渲染、数据是缓存还是未缓存,以及请求是初始访问还是后续导航的一部分。根据你的使用案例,你可以为各个路由和数据请求配置缓存行为。

¥Caching behavior changes depending on whether the route is statically or dynamically rendered, data is cached or uncached, and whether a request is part of an initial visit or a subsequent navigation. Depending on your use case, you can configure the caching behavior for individual routes and data requests.

请求记忆

¥Request Memoization

React 扩展了 fetch API 以自动记住具有相同 URL 和选项的请求。这意味着你可以在 React 组件树中的多个位置调用相同数据的获取函数,而只需执行一次。

¥React extends the fetch API to automatically memoize requests that have the same URL and options. This means you can call a fetch function for the same data in multiple places in a React component tree while only executing it once.

例如,如果你需要跨路由(例如在布局、页面和多个组件中)使用相同的数据,则不必在树的顶部获取数据,并在组件之间转发 props。相反,你可以在需要的组件中获取数据,而不必担心通过网络对同一数据发出多个请求对性能的影响。

¥For example, if you need to use the same data across a route (e.g. in a Layout, Page, and multiple components), you do not have to fetch data at the top of the tree, and forward props between components. Instead, you can fetch data in the components that need it without worrying about the performance implications of making multiple requests across the network for the same data.

async function getItem() {
// The `fetch` function is automatically memoized and the result
// is cached
const res = await fetch('https://.../item/1')
return res.json()
}

// This function is called twice, but only executed the first time
const item = await getItem() // cache MISS

// The second call could be anywhere in your route
const item = await getItem() // cache HIT
async function getItem() {
// The `fetch` function is automatically memoized and the result
// is cached
const res = await fetch('https://.../item/1')
return res.json()
}

// This function is called twice, but only executed the first time
const item = await getItem() // cache MISS

// The second call could be anywhere in your route
const item = await getItem() // cache HIT

请求记忆如何工作

¥How Request Memoization Works

  • 渲染路由时,第一次调用特定请求时,其结果不会在内存中,而是缓存 MISS

    ¥While rendering a route, the first time a particular request is called, its result will not be in memory and it'll be a cache MISS.

  • 因此,该函数将被执行,并且将从外部源获取数据,并将结果存储在内存中。

    ¥Therefore, the function will be executed, and the data will be fetched from the external source, and the result will be stored in memory.

  • 同一渲染通道中请求的后续函数调用将是缓存 HIT,数据将从内存中返回,而不执行该函数。

    ¥Subsequent function calls of the request in the same render pass will be a cache HIT, and the data will be returned from memory without executing the function.

  • 一旦路由被渲染并且渲染通道完成,内存为 "reset" 并且所有请求记忆条目都被清除。

    ¥Once the route has been rendered and the rendering pass is complete, memory is "reset" and all request memoization entries are cleared.

很高兴知道:

¥Good to know:

  • 请求记忆是一项 React 功能,而不是 Next.js 功能。它包含在这里是为了展示它如何与其他缓存机制交互。

    ¥Request memoization is a React feature, not a Next.js feature. It's included here to show how it interacts with the other caching mechanisms.

  • 记忆化仅适用于 fetch 请求中的 GET 方法。

    ¥Memoization only applies to the GET method in fetch requests.

  • 记忆化仅适用于 React 组件树,这意味着:

    ¥Memoization only applies to the React Component tree, this means:

    • 它适用于 generateMetadatagenerateStaticParams、布局、页面和其他服务器组件中的 fetch 请求。

      ¥It applies to fetch requests in generateMetadata, generateStaticParams, Layouts, Pages, and other Server Components.

    • 它不适用于路由处理程序中的 fetch 请求,因为它们不是 React 组件树的一部分。

      ¥It doesn't apply to fetch requests in Route Handlers as they are not a part of the React component tree.

  • 对于 fetch 不适合的情况(例如某些数据库客户端、CMS 客户端或 GraphQL 客户端),你可以使用 React cache 函数 来记忆函数。

    ¥For cases where fetch is not suitable (e.g. some database clients, CMS clients, or GraphQL clients), you can use the React cache function to memoize functions.

期间

¥Duration

缓存会持续服务器请求的生命周期,直到 React 组件树完成渲染。

¥The cache lasts the lifetime of a server request until the React component tree has finished rendering.

重新验证

¥Revalidating

由于记忆不会在服务器请求之间共享,并且仅在渲染期间应用,因此无需重新验证它。

¥Since the memoization is not shared across server requests and only applies during rendering, there is no need to revalidate it.

选择退出

¥Opting out

记忆仅适用于 fetch 请求中的 GET 方法,其他方法(例如 POSTDELETE)不会被记忆。此默认行为是 React 优化,我们不建议选择退出。

¥Memoization only applies to the GET method in fetch requests, other methods, such as POST and DELETE, are not memoized. This default behavior is a React optimization and we do not recommend opting out of it.

要管理单个请求,你可以使用 AbortController 中的 signal 属性。然而,这不会选择不记忆请求,而是中止正在进行的请求。

¥To manage individual requests, you can use the signal property from AbortController. However, this will not opt requests out of memoization, rather, abort in-flight requests.

const { signal } = new AbortController()
fetch(url, { signal })

数据缓存

¥Data Cache

Next.js 有一个内置的数据缓存,可以在传入的服务器请求和部署中保留数据获取的结果。这是可能的,因为 Next.js 扩展了原生 fetch API,以允许服务器上的每个请求设置自己的持久缓存语义。

¥Next.js has a built-in Data Cache that persists the result of data fetches across incoming server requests and deployments. This is possible because Next.js extends the native fetch API to allow each request on the server to set its own persistent caching semantics.

很高兴知道:在浏览器中,fetchcache 选项指示请求如何与浏览器的 HTTP 缓存交互,在 Next.js 中,cache 选项指示服务器端请求如何与服务器的数据缓存交互。

¥Good to know: In the browser, the cache option of fetch indicates how a request will interact with the browser's HTTP cache, in Next.js, the cache option indicates how a server-side request will interact with the server's Data Cache.

默认情况下,缓存使用 fetch 的数据请求。你可以使用 fetchcachenext.revalidate 选项来配置缓存行为。

¥By default, data requests that use fetch are cached. You can use the cache and next.revalidate options of fetch to configure the caching behavior.

数据缓存的工作原理

¥How the Data Cache Works

  • 渲染期间第一次调用 fetch 请求时,Next.js 会检查数据缓存中是否有缓存的响应。

    ¥The first time a fetch request is called during rendering, Next.js checks the Data Cache for a cached response.

  • 如果找到缓存的响应,则会立即返回并 memoized

    ¥If a cached response is found, it's returned immediately and memoized.

  • 如果未找到缓存的响应,则会向数据源发出请求,将结果存储在数据缓存中并进行记忆。

    ¥If a cached response is not found, the request is made to the data source, the result is stored in the Data Cache, and memoized.

  • 对于未缓存的数据(例如 { cache: 'no-store' }),始终从数据源获取结果并进行记忆。

    ¥For uncached data (e.g. { cache: 'no-store' }), the result is always fetched from the data source, and memoized.

  • 无论数据是缓存还是未缓存,请求始终会被记忆,以避免在 React 渲染过程中对相同数据发出重复请求。

    ¥Whether the data is cached or uncached, the requests are always memoized to avoid making duplicate requests for the same data during a React render pass.

数据缓存和请求记忆之间的差异

¥Differences between the Data Cache and Request Memoization

虽然这两种缓存机制都有助于通过重用缓存数据来提高性能,但数据缓存在传入请求和部署中是持久的,而记忆仅持续请求的生命周期。

¥While both caching mechanisms help improve performance by re-using cached data, the Data Cache is persistent across incoming requests and deployments, whereas memoization only lasts the lifetime of a request.

通过记忆,我们减少了同一渲染通道中必须跨越网络边界从渲染服务器到数据缓存服务器(例如 CDN 或边缘网络)或数据源(例如数据库或 CMS)的重复请求的数量。通过数据缓存,我们减少了对原始数据源发出的请求数量。

¥With memoization, we reduce the number of duplicate requests in the same render pass that have to cross the network boundary from the rendering server to the Data Cache server (e.g. a CDN or Edge Network) or data source (e.g. a database or CMS). With the Data Cache, we reduce the number of requests made to our origin data source.

期间

¥Duration

数据缓存在传入请求和部署中保持不变,除非你重新验证或选择退出。

¥The Data Cache is persistent across incoming requests and deployments unless you revalidate or opt-out.

重新验证

¥Revalidating

缓存的数据可以通过两种方式重新验证:

¥Cached data can be revalidated in two ways, with:

  • 基于时间的重新验证:经过一定时间并发出新请求后重新验证数据。这对于不经常更改且新鲜度不那么重要的数据非常有用。

    ¥Time-based Revalidation: Revalidate data after a certain amount of time has passed and a new request is made. This is useful for data that changes infrequently and freshness is not as critical.

  • 按需重新验证:根据事件(例如表单提交)重新验证数据。按需重新验证可以使用基于标签或基于路径的方法立即重新验证数据组。当你想要确保尽快显示最新数据时(例如,当更新无头 CMS 的内容时),这非常有用。

    ¥On-demand Revalidation: Revalidate data based on an event (e.g. form submission). On-demand revalidation can use a tag-based or path-based approach to revalidate groups of data at once. This is useful when you want to ensure the latest data is shown as soon as possible (e.g. when content from your headless CMS is updated).

基于时间的重新验证

¥Time-based Revalidation

要定期重新验证数据,你可以使用 fetchnext.revalidate 选项来设置资源的缓存生命周期(以秒为单位)。

¥To revalidate data at a timed interval, you can use the next.revalidate option of fetch to set the cache lifetime of a resource (in seconds).

// Revalidate at most every hour
fetch('https://...', { next: { revalidate: 3600 } })

或者,你可以使用 路由段配置选项 来配置分段中的所有 fetch 请求,或者在无法使用 fetch 的情况下。

¥Alternatively, you can use Route Segment Config options to configure all fetch requests in a segment or for cases where you're not able to use fetch.

基于时间的重新验证如何运作

¥How Time-based Revalidation Works

  • 第一次调用 revalidate 的获取请求时,将从外部数据源获取数据并将其存储在数据缓存中。

    ¥The first time a fetch request with revalidate is called, the data will be fetched from the external data source and stored in the Data Cache.

  • 在指定时间范围(例如 60 秒)内调用的任何请求都将返回缓存的数据。

    ¥Any requests that are called within the specified timeframe (e.g. 60-seconds) will return the cached data.

  • 在该时间范围之后,下一个请求仍将返回缓存的(现已过时)数据。

    ¥After the timeframe, the next request will still return the cached (now stale) data.

    • Next.js 将在后台触发数据的重新验证。

      ¥Next.js will trigger a revalidation of the data in the background.

    • 成功获取数据后,Next.js 将使用新数据更新数据缓存。

      ¥Once the data is fetched successfully, Next.js will update the Data Cache with the fresh data.

    • 如果后台重新验证失败,之前的数据将保持不变。

      ¥If the background revalidation fails, the previous data will be kept unaltered.

这与 stale-while-revalidate 行为类似。

¥This is similar to stale-while-revalidate behavior.

按需重新验证

¥On-demand Revalidation

数据可以按路径 (revalidatePath) 或缓存标记 (revalidateTag) 按需重新验证。

¥Data can be revalidated on-demand by path (revalidatePath) or by cache tag (revalidateTag).

按需重新验证的工作原理

¥How On-Demand Revalidation Works

  • 第一次调用 fetch 请求时,将从外部数据源获取数据并将其存储在数据缓存中。

    ¥The first time a fetch request is called, the data will be fetched from the external data source and stored in the Data Cache.

  • 当触发按需重新验证时,将从缓存中清除相应的缓存条目。

    ¥When an on-demand revalidation is triggered, the appropriate cache entries will be purged from the cache.

    • 这与基于时间的重新验证不同,基于时间的重新验证将旧数据保留在缓存中,直到获取新数据。

      ¥This is different from time-based revalidation, which keeps the stale data in the cache until the fresh data is fetched.

  • 下次请求时,又是缓存 MISS,从外部数据源取出数据,存入 Data Cache。

    ¥The next time a request is made, it will be a cache MISS again, and the data will be fetched from the external data source and stored in the Data Cache.

选择退出

¥Opting out

对于单个数据提取,你可以通过将 cache 选项设置为 no-store 来选择退出缓存。这意味着每当调用 fetch 时就会获取数据。

¥For individual data fetches, you can opt out of caching by setting the cache option to no-store. This means data will be fetched whenever fetch is called.

// Opt out of caching for an individual `fetch` request
fetch(`https://...`, { cache: 'no-store' })

或者,你也可以使用 路由段配置选项 选择退出特定路由段的缓存。这将影响路由段中的所有数据请求,包括第三方库。

¥Alternatively, you can also use the Route Segment Config options to opt out of caching for a specific route segment. This will affect all data requests in the route segment, including third-party libraries.

// Opt out of caching for all data requests in the route segment
export const dynamic = 'force-dynamic'

注意:数据缓存目前仅在页面/路由中可用,在中间件中不可用。默认情况下,在中间件内部完成的任何提取都不会被缓存。

¥Note: Data Cache is currently only available in pages/routes, not middleware. Any fetches done inside of your middleware will be uncached by default.

Vercel 数据缓存

¥Vercel Data Cache

如果你的 Next.js 应用部署到 Vercel,我们建议你阅读 Vercel 数据缓存 文档,以便更好地了解 Vercel 的特定功能。

¥If your Next.js application is deployed to Vercel, we recommend reading the Vercel Data Cache documentation for a better understanding of Vercel specific features.

全路由缓存

¥Full Route Cache

相关术语:

¥Related terms:

你可能会看到术语“自动静态优化”、“静态站点生成”或“静态渲染”可互换使用,指的是应用在构建时的渲染和缓存路由的过程。

¥You may see the terms Automatic Static Optimization, Static Site Generation, or Static Rendering being used interchangeably to refer to the process of rendering and caching routes of your application at build time.

Next.js 在构建时自动渲染和缓存路由。这是一项优化,允许你为每个请求提供缓存的路由,而不是在服务器上渲染,从而加快页面加载速度。

¥Next.js automatically renders and caches routes at build time. This is an optimization that allows you to serve the cached route instead of rendering on the server for every request, resulting in faster page loads.

要了解完整路由缓存的工作原理,了解 React 如何处理渲染以及 Next.js 如何缓存结果会很有帮助:

¥To understand how the Full Route Cache works, it's helpful to look at how React handles rendering, and how Next.js caches the result:

1. 在服务器上进行 React 渲染

¥ React Rendering on the Server

在服务器上,Next.js 使用 React 的 API 来编排渲染。渲染工作被分成几个块:通过单独的路由段和悬念边界。

¥On the server, Next.js uses React's APIs to orchestrate rendering. The rendering work is split into chunks: by individual routes segments and Suspense boundaries.

每个块都分两步渲染:

¥Each chunk is rendered in two steps:

  1. React 将服务器组件渲染为一种特殊的数据格式,针对流进行了优化,称为“React 服务器组件有效负载”。

    ¥React renders Server Components into a special data format, optimized for streaming, called the React Server Component Payload.

  2. Next.js 使用 React 服务器组件有效负载和客户端组件 JavaScript 指令在服务器上渲染 HTML。

    ¥Next.js uses the React Server Component Payload and Client Component JavaScript instructions to render HTML on the server.

这意味着我们不必等待所有内容都渲染完毕才能缓存工作或发送响应。相反,我们可以在工作完成时流式传输响应。

¥This means we don't have to wait for everything to render before caching the work or sending a response. Instead, we can stream a response as work is completed.

什么是 React 服务器组件有效负载?

¥What is the React Server Component Payload?

React Server 组件有效负载是渲染的 React Server 组件树的紧凑二进制表示。客户端上的 React 使用它来更新浏览器的 DOM。React 服务器组件有效负载包含:

¥The React Server Component Payload is a compact binary representation of the rendered React Server Components tree. It's used by React on the client to update the browser's DOM. The React Server Component Payload contains:

  • Server Components 的渲染结果

    ¥The rendered result of Server Components

  • 客户端组件应渲染的位置的占位符以及对其 JavaScript 文件的引用

    ¥Placeholders for where Client Components should be rendered and references to their JavaScript files

  • 从服务器组件传递到客户端组件的任何属性

    ¥Any props passed from a Server Component to a Client Component

要了解更多信息,请参阅 服务器组件 文档。

¥To learn more, see the Server Components documentation.

2. Next.js 在服务器上缓存(全路由缓存)

¥ Next.js Caching on the Server (Full Route Cache)

Next.js 的默认行为是在服务器上缓存路由的渲染结果(React 服务器组件负载和 HTML)。这适用于构建时或重新验证期间静态渲染的路由。

¥The default behavior of Next.js is to cache the rendered result (React Server Component Payload and HTML) of a route on the server. This applies to statically rendered routes at build time, or during revalidation.

3. 在客户端上进行水合作用和协调

¥ React Hydration and Reconciliation on the Client

在请求时,在客户端上:

¥At request time, on the client:

  1. HTML 用于立即显示客户端和服务器组件的快速非交互式初始预览。

    ¥The HTML is used to immediately show a fast non-interactive initial preview of the Client and Server Components.

  2. React 服务器组件有效负载用于协调客户端和渲染的服务器组件树,并更新 DOM。

    ¥The React Server Components Payload is used to reconcile the Client and rendered Server Component trees, and update the DOM.

  3. JavaScript 指令用于 hydrate 客户端组件并使应用具有交互性。

    ¥The JavaScript instructions are used to hydrate Client Components and make the application interactive.

4. Next.js 客户端缓存(路由缓存)

¥ Next.js Caching on the Client (Router Cache)

React Server Component Payload 存储在客户端 路由缓存 中 - 一个单独的内存缓存,由单独的路由段分割。该路由缓存用于通过存储以前访问过的路由并预取未来的路由来改善导航体验。

¥The React Server Component Payload is stored in the client-side Router Cache - a separate in-memory cache, split by individual route segment. This Router Cache is used to improve the navigation experience by storing previously visited routes and prefetching future routes.

5. 后续导航

¥ Subsequent Navigations

在后续导航或预取期间,Next.js 将检查 React Server 组件有效负载是否存储在路由缓存中。如果是这样,它将跳过向服务器发送新请求。

¥On subsequent navigations or during prefetching, Next.js will check if the React Server Components Payload is stored in the Router Cache. If so, it will skip sending a new request to the server.

如果路由段不在缓存中,Next.js 将从服务器获取 React Server Components Payload,并填充客户端上的路由缓存。

¥If the route segments are not in the cache, Next.js will fetch the React Server Components Payload from the server, and populate the Router Cache on the client.

静态和动态渲染

¥Static and Dynamic Rendering

路由在构建时是否被缓存取决于它是静态渲染还是动态渲染。默认情况下会缓存静态路由,而动态路由会在请求时渲染,并且不会被缓存。

¥Whether a route is cached or not at build time depends on whether it's statically or dynamically rendered. Static routes are cached by default, whereas dynamic routes are rendered at request time, and not cached.

下图显示了静态和动态渲染的路由之间的差异,以及缓存和未缓存的数据:

¥This diagram shows the difference between statically and dynamically rendered routes, with cached and uncached data:

了解有关 静态和动态渲染 的更多信息。

¥Learn more about static and dynamic rendering.

期间

¥Duration

默认情况下,完整路由缓存是持久的。这意味着渲染输出会跨用户请求进行缓存。

¥By default, the Full Route Cache is persistent. This means that the render output is cached across user requests.

无效

¥Invalidation

有两种方法可以使完整路由缓存失效:

¥There are two ways you can invalidate the Full Route Cache:

  • 重新验证数据:重新验证 数据缓存 将通过在服务器上重新渲染组件并缓存新的渲染输出来依次使路由缓存失效。

    ¥**Revalidating Data**: Revalidating the Data Cache, will in turn invalidate the Router Cache by re-rendering components on the server and caching the new render output.

  • 重新部署:与跨部署持续存在的数据缓存不同,完整路由缓存在新部署中会被清除。

    ¥Redeploying: Unlike the Data Cache, which persists across deployments, the Full Route Cache is cleared on new deployments.

选择退出

¥Opting out

你可以选择退出完整路由缓存,或者换句话说,为每个传入请求动态渲染组件,方法是:

¥You can opt out of the Full Route Cache, or in other words, dynamically render components for every incoming request, by:

  • 使用 动态功能:这将从完整路由缓存中选择路由并在请求时动态渲染它。数据缓存仍然可以使用。

    ¥Using a Dynamic Function: This will opt the route out from the Full Route Cache and dynamically render it at request time. The Data Cache can still be used.

  • 使用 dynamic = 'force-dynamic'revalidate = 0 路由段配置选项:这将跳过完整路由缓存和数据缓存。这意味着将在每次传入服务器的请求时渲染组件并获取数据。路由缓存仍然适用,因为它是客户端缓存。

    ¥Using the dynamic = 'force-dynamic' or revalidate = 0 route segment config options: This will skip the Full Route Cache and the Data Cache. Meaning components will be rendered and data fetched on every incoming request to the server. The Router Cache will still apply as it's a client-side cache.

  • 选择退出 数据缓存:如果路由有未缓存的 fetch 请求,则会从完整路由缓存中选择该路由。将为每个传入请求获取特定 fetch 请求的数据。其他未选择退出缓存的 fetch 请求仍将缓存在数据缓存中。这允许缓存和未缓存数据的混合。

    ¥Opting out of the Data Cache: If a route has a fetch request that is not cached, this will opt the route out of the Full Route Cache. The data for the specific fetch request will be fetched for every incoming request. Other fetch requests that do not opt out of caching will still be cached in the Data Cache. This allows for a hybrid of cached and uncached data.

路由缓存

¥Router Cache

相关术语:

¥Related Terms:

你可能会看到路由缓存被称为客户端缓存或预取缓存。预取缓存指的是预取的路由段,而客户端缓存指的是整个路由缓存,其中包括已访问的路由段和预取的路由段。此缓存专门适用于 Next.js 和服务器组件,与浏览器的 bfcache 不同,尽管它具有类似的结果。

¥You may see the Router Cache being referred to as Client-side Cache or Prefetch Cache. While Prefetch Cache refers to the prefetched route segments, Client-side Cache refers to the whole Router cache, which includes both visited and prefetched segments. This cache specifically applies to Next.js and Server Components, and is different to the browser's bfcache, though it has a similar result.

Next.js 有一个内存客户端缓存,用于在用户会话期间存储 React 服务器组件有效负载,并按各个路由段分割。这称为路由缓存。

¥Next.js has an in-memory client-side cache that stores the React Server Component Payload, split by individual route segments, for the duration of a user session. This is called the Router Cache.

路由缓存的工作原理

¥How the Router Cache Works

当用户在路由之间导航时,Next.js 会缓存访问过的路由段和 prefetches 用户可能导航到的路由(基于其视口中的 <Link> 组件)。

¥As a user navigates between routes, Next.js caches visited route segments and prefetches the routes the user is likely to navigate to (based on <Link> components in their viewport).

这将为用户带来更好的导航体验:

¥This results in an improved navigation experience for the user:

  • 即时向后/向前导航,因为访问的路由已被缓存,并且由于预取和 部分渲染,可以快速导航到新路由。

    ¥Instant backward/forward navigation because visited routes are cached and fast navigation to new routes because of prefetching and partial rendering.

  • 导航之间不会重新加载整页,并且会保留 React 状态和浏览器状态。

    ¥No full-page reload between navigations, and React state and browser state are preserved.

路由缓存和全路由缓存的区别:

¥Difference between the Router Cache and Full Route Cache:

路由缓存在用户会话期间将 React 服务器组件有效负载临时存储在浏览器中,而完整路由缓存则跨多个用户请求将 React 服务器组件有效负载和 HTML 持久存储在服务器上。

¥The Router Cache temporarily stores the React Server Component Payload in the browser for the duration of a user session, whereas the Full Route Cache persistently stores the React Server Component Payload and HTML on the server across multiple user requests.

完整路由缓存仅缓存静态渲染的路由,而路由缓存适用于静态和动态渲染的路由。

¥While the Full Route Cache only caches statically rendered routes, the Router Cache applies to both statically and dynamically rendered routes.

期间

¥Duration

缓存存储在浏览器的临时内存中。有两个因素决定路由缓存的持续时间:

¥The cache is stored in the browser's temporary memory. Two factors determine how long the router cache lasts:

  • 会议:缓存在整个导航过程中持续存在。但是,它会在页面刷新时被清除。

    ¥Session: The cache persists across navigation. However, it's cleared on page refresh.

  • 自动失效期:单个段的缓存在特定时间后自动失效。持续时间取决于资源 prefetched 的情况:

    ¥Automatic Invalidation Period: The cache of an individual segment is automatically invalidated after a specific time. The duration depends on how the resource was prefetched:

    • 默认预取(prefetch={null} 或未指定):30 秒

      ¥Default Prefetching (prefetch={null} or unspecified): 30 seconds

    • 完全预取:(prefetch={true}router.prefetch):5 分钟

      ¥Full Prefetching: (prefetch={true} or router.prefetch): 5 minutes

虽然页面刷新将清除所有缓存的段,但自动失效期仅影响预取后的各个段。

¥While a page refresh will clear all cached segments, the automatic invalidation period only affects the individual segment from the time it was prefetched.

注意:自 v14.2.0-canary.53 起,有 实验支持 用于配置这些值。

¥Note: There is experimental support for configuring these values as of v14.2.0-canary.53.

无效

¥Invalidation

有两种方法可以使路由缓存失效:

¥There are two ways you can invalidate the Router Cache:

  • 在服务器操作中:

    ¥In a Server Action:

  • 调用 router.refresh 将使 Router Cache 失效并向服务器发出当前路由的新请求。

    ¥Calling router.refresh will invalidate the Router Cache and make a new request to the server for the current route.

选择退出

¥Opting out

无法选择退出路由缓存。但是,你可以通过调用 router.refreshrevalidatePathrevalidateTag 使其无效(见上文)。这将清除缓存并向服务器发出新请求,确保显示最新数据。

¥It's not possible to opt out of the Router Cache. However, you can invalidate it by calling router.refresh, revalidatePath, or revalidateTag (see above). This will clear the cache and make a new request to the server, ensuring the latest data is shown.

你还可以通过将 <Link> 组件的 prefetch 属性设置为 false 来选择退出预取。但是,这仍会临时存储路由段 30 秒,以允许嵌套段之间的即时导航,例如选项卡栏或前后导航。访问过的路由仍将被缓存。

¥You can also opt out of prefetching by setting the prefetch prop of the <Link> component to false. However, this will still temporarily store the route segments for 30s to allow instant navigation between nested segments, such as tab bars, or back and forward navigation. Visited routes will still be cached.

缓存交互

¥Cache Interactions

配置不同的缓存机制时,了解它们如何相互作用非常重要:

¥When configuring the different caching mechanisms, it's important to understand how they interact with each other:

数据缓存和全路由缓存

¥Data Cache and Full Route Cache

  • 重新验证或选择退出数据缓存将使完整路由缓存无效,因为渲染输出取决于数据。

    ¥Revalidating or opting out of the Data Cache will invalidate the Full Route Cache, as the render output depends on data.

  • 使完整路由缓存失效或选择退出不会影响数据缓存。你可以动态渲染具有缓存和未缓存数据的路由。当你的大部分页面使用缓存数据,但你有一些组件依赖于需要在请求时获取的数据时,这非常有用。你可以动态渲染,而不必担心重新获取所有数据对性能的影响。

    ¥Invalidating or opting out of the Full Route Cache does not affect the Data Cache. You can dynamically render a route that has both cached and uncached data. This is useful when most of your page uses cached data, but you have a few components that rely on data that needs to be fetched at request time. You can dynamically render without worrying about the performance impact of re-fetching all the data.

数据缓存和客户端路由缓存

¥Data Cache and Client-side Router cache

  • 重新验证 路由处理程序 中的数据缓存不会立即使路由缓存失效,因为路由处理程序未绑定到特定路由。这意味着路由缓存将继续提供先前的有效负载,直到硬刷新或自动失效期结束。

    ¥Revalidating the Data Cache in a Route Handler will not immediately invalidate the Router Cache as the Route Handler isn't tied to a specific route. This means Router Cache will continue to serve the previous payload until a hard refresh, or the automatic invalidation period has elapsed.

  • 要立即使数据缓存和路由缓存失效,可以在 服务器动作 中使用 revalidatePathrevalidateTag

    ¥To immediately invalidate the Data Cache and Router cache, you can use revalidatePath or revalidateTag in a Server Action.

APIs

下表概述了不同的 Next.js API 如何影响缓存:

¥The following table provides an overview of how different Next.js APIs affect caching:

API路由缓存全路由缓存数据缓存反应缓存
<Link prefetch>缓存
router.prefetch缓存
router.refresh重新验证
fetch缓存缓存
fetch options.cache缓存或选择退出
fetch options.next.revalidate重新验证重新验证
fetch options.next.tags缓存缓存
revalidateTag重新验证(服务器操作)重新验证重新验证
revalidatePath重新验证(服务器操作)重新验证重新验证
const revalidate重新验证或选择退出重新验证或选择退出
const dynamic缓存或选择退出缓存或选择退出
cookies重新验证(服务器操作)选择退出
headers, searchParams选择退出
generateStaticParams缓存
React.cache缓存
unstable_cache

默认情况下,<Link> 组件自动从完整路由缓存中预取路由,并将 React Server 组件有效负载添加到路由缓存中。

¥By default, the <Link> component automatically prefetches routes from the Full Route Cache and adds the React Server Component Payload to the Router Cache.

要禁用预取,你可以将 prefetch 属性设置为 false。但这不会永久跳过缓存,当用户访问该路由时,该路由段仍会缓存在客户端。

¥To disable prefetching, you can set the prefetch prop to false. But this will not skip the cache permanently, the route segment will still be cached client-side when the user visits the route.

了解有关 <Link> 组件 的更多信息。

¥Learn more about the <Link> component.

router.prefetch

useRouter 钩子的 prefetch 选项可用于手动预取路由。这会将 React 服务器组件有效负载添加到路由缓存中。

¥The prefetch option of the useRouter hook can be used to manually prefetch a route. This adds the React Server Component Payload to the Router Cache.

请参阅 useRouter API 参考。

¥See the useRouter hook API reference.

router.refresh

useRouter 钩子的 refresh 选项可用于手动刷新路由。这将完全清除路由缓存,并向服务器发出当前路由的新请求。refresh 不影响数据或完整路由缓存。

¥The refresh option of the useRouter hook can be used to manually refresh a route. This completely clears the Router Cache, and makes a new request to the server for the current route. refresh does not affect the Data or Full Route Cache.

渲染的结果将在客户端上进行协调,同时保留 React 状态和浏览器状态。

¥The rendered result will be reconciled on the client while preserving React state and browser state.

请参阅 useRouter API 参考。

¥See the useRouter hook API reference.

fetch

fetch 返回的数据自动缓存在数据缓存中。

¥Data returned from fetch is automatically cached in the Data Cache.

// Cached by default. `force-cache` is the default option and can be omitted.
fetch(`https://...`, { cache: 'force-cache' })

请参阅 fetch API 参考 了解更多选项。

¥See the fetch API Reference for more options.

fetch options.cache

你可以通过将 cache 选项设置为 no-store 来选择退出单个 fetch 数据缓存请求:

¥You can opt out individual fetch requests of data caching by setting the cache option to no-store:

// Opt out of caching
fetch(`https://...`, { cache: 'no-store' })

由于渲染输出依赖于数据,因此使用 cache: 'no-store' 也会跳过使用 fetch 请求的路由的完整路由缓存。也就是说,该路由将在每个请求中动态渲染,但你仍然可以在同一路由中拥有其他缓存的数据请求。

¥Since the render output depends on data, using cache: 'no-store' will also skip the Full Route Cache for the route where the fetch request is used. That is, the route will be dynamically rendered every request, but you can still have other cached data requests in the same route.

请参阅 fetch API 参考 了解更多选项。

¥See the fetch API Reference for more options.

fetch options.next.revalidate

你可以使用 fetchnext.revalidate 选项来设置单个 fetch 请求的重新验证期限(以秒为单位)。这将重新验证数据缓存,进而重新验证完整路由缓存。将获取新数据,并且组件将在服务器上重新渲染。

¥You can use the next.revalidate option of fetch to set the revalidation period (in seconds) of an individual fetch request. This will revalidate the Data Cache, which in turn will revalidate the Full Route Cache. Fresh data will be fetched, and components will be re-rendered on the server.

// Revalidate at most after 1 hour
fetch(`https://...`, { next: { revalidate: 3600 } })

请参阅 fetch API 参考 了解更多选项。

¥See the fetch API reference for more options.

fetch options.next.tagsrevalidateTag

¥fetch options.next.tags and revalidateTag

Next.js 有一个缓存标记系统,用于细粒度数据缓存和重新验证。

¥Next.js has a cache tagging system for fine-grained data caching and revalidation.

  1. 使用 fetchunstable_cache 时,你可以选择使用一个或多个标签来标记缓存条目。

    ¥When using fetch or unstable_cache, you have the option to tag cache entries with one or more tags.

  2. 然后,你可以调用 revalidateTag 来清除与该标记关联的缓存条目。

    ¥Then, you can call revalidateTag to purge the cache entries associated with that tag.

例如,你可以在获取数据时设置标签:

¥For example, you can set a tag when fetching data:

// Cache data with a tag
fetch(`https://...`, { next: { tags: ['a', 'b', 'c'] } })

然后,使用标签调用 revalidateTag 来清除缓存条目:

¥Then, call revalidateTag with a tag to purge the cache entry:

// Revalidate entries with a specific tag
revalidateTag('a')

你可以在两个地方使用 revalidateTag,具体取决于你想要实现的目标:

¥There are two places you can use revalidateTag, depending on what you're trying to achieve:

  1. 路由处理程序 - 重新验证数据以响应第三方事件(例如 webhook)。这不会立即使路由缓存失效,因为路由处理程序未绑定到特定路由。

    ¥Route Handlers - to revalidate data in response of a third party event (e.g. webhook). This will not invalidate the Router Cache immediately as the Router Handler isn't tied to a specific route.

  2. 服务器操作 - 在用户操作(例如表单提交)后重新验证数据。这将使关联路由的路由缓存无效。

    ¥Server Actions - to revalidate data after a user action (e.g. form submission). This will invalidate the Router Cache for the associated route.

revalidatePath

revalidatePath 允许你在一次操作中手动重新验证数据并重新渲染特定路径下方的路由段。调用 revalidatePath 方法会使数据缓存重新生效,从而使完整路由缓存失效。

¥revalidatePath allows you manually revalidate data and re-render the route segments below a specific path in a single operation. Calling the revalidatePath method revalidates the Data Cache, which in turn invalidates the Full Route Cache.

revalidatePath('/')

你可以在两个地方使用 revalidatePath,具体取决于你想要实现的目标:

¥There are two places you can use revalidatePath, depending on what you're trying to achieve:

  1. 路由处理程序 - 重新验证数据以响应第三方事件(例如 Webhook)。

    ¥Route Handlers - to revalidate data in response to a third party event (e.g. webhook).

  2. 服务器操作 - 在用户交互(例如提交表单、单击按钮)后重新验证数据。

    ¥Server Actions - to revalidate data after a user interaction (e.g. form submission, clicking a button).

请参阅 revalidatePath API 参考 了解更多信息。

¥See the revalidatePath API reference for more information.

revalidatePathrouter.refresh

¥**revalidatePath** vs. router.refresh:

调用 router.refresh 将清除路由缓存,并在服务器上重新渲染路由段,而不会使数据缓存或完整路由缓存失效。

¥Calling router.refresh will clear the Router cache, and re-render route segments on the server without invalidating the Data Cache or the Full Route Cache.

不同之处在于,revalidatePath 会清除数据缓存和全路由缓存,而 router.refresh() 不会更改数据缓存和全路由缓存,因为它是客户端 API。

¥The difference is that revalidatePath purges the Data Cache and Full Route Cache, whereas router.refresh() does not change the Data Cache and Full Route Cache, as it is a client-side API.

动态函数

¥Dynamic Functions

诸如 cookiesheaders 之类的动态函数以及 Pages 中的 searchParams 属性取决于运行时传入请求信息。使用它们将从完整路由缓存中选择一条路由,换句话说,该路由将被动态渲染。

¥Dynamic functions like cookies and headers, and the searchParams prop in Pages depend on runtime incoming request information. Using them will opt a route out of the Full Route Cache, in other words, the route will be dynamically rendered.

cookies

在服务器操作中使用 cookies.setcookies.delete 会使路由缓存失效,以防止使用 cookie 的路由变得过时(例如,反映身份验证更改)。

¥Using cookies.set or cookies.delete in a Server Action invalidates the Router Cache to prevent routes that use cookies from becoming stale (e.g. to reflect authentication changes).

请参阅 cookies API 参考。

¥See the cookies API reference.

片段配置选项

¥Segment Config Options

路由段配置选项可用于覆盖路由段默认值,或者当你无法使用 fetch API(例如数据库客户端或第三方库)时。

¥The Route Segment Config options can be used to override the route segment defaults or when you're not able to use the fetch API (e.g. database client or 3rd party libraries).

以下路由段配置选项将选择退出数据缓存和完整路由缓存:

¥The following Route Segment Config options will opt out of the Data Cache and Full Route Cache:

  • const dynamic = 'force-dynamic'

  • const revalidate = 0

有关更多选项,请参阅 路由片段配置 文档。

¥See the Route Segment Config documentation for more options.

generateStaticParams

对于 动态片段(例如 app/blog/[slug]/page.js),generateStaticParams 提供的路径在构建时缓存在完整路由缓存中。在请求时,Next.js 还将缓存第一次访问时构建时未知的路径。

¥For dynamic segments (e.g. app/blog/[slug]/page.js), paths provided by generateStaticParams are cached in the Full Route Cache at build time. At request time, Next.js will also cache paths that weren't known at build time the first time they're visited.

你可以通过在路由段中使用 export const dynamicParams = false 选项在请求时禁用缓存。使用此配置选项时,仅提供 generateStaticParams 提供的路径,其他路由将返回 404 或匹配(在 包罗万象的路由 的情况下)。

¥You can disable caching at request time by using export const dynamicParams = false option in a route segment. When this config option is used, only paths provided by generateStaticParams will be served, and other routes will 404 or match (in the case of catch-all routes).

参见 generateStaticParams API 参考

¥See the generateStaticParams API reference.

React cache 函数

¥React cache function

React cache 函数允许你记住函数的返回值,允许你多次调用同一个函数,而只执行一次。

¥The React cache function allows you to memoize the return value of a function, allowing you to call the same function multiple times while only executing it once.

由于 fetch 请求会自动记忆,因此你无需将其封装在 React cache 中。但是,当 fetch API 不适合时,你可以使用 cache 手动记忆用例的数据请求。例如,某些数据库客户端、CMS 客户端或 GraphQL 客户端。

¥Since fetch requests are automatically memoized, you do not need to wrap it in React cache. However, you can use cache to manually memoize data requests for use cases when the fetch API is not suitable. For example, some database clients, CMS clients, or GraphQL clients.

import { cache } from 'react'
import db from '@/lib/db'

export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
import { cache } from 'react'
import db from '@/lib/db'

export const getItem = cache(async (id) => {
const item = await db.item.findUnique({ id })
return item
})