SkyBlog

©2020 - 2026 By Flysky
框架Next.js|主题Shadcn
Built by vercel on Mon, 19 Jan 2026 11:01:56 GMT

Next.js 中的缓存

Next.js 通过缓存渲染工作和数据请求来提升应用性能并降低成本

这篇文章发布于 2025年08月29日,星期五,08:38。阅读 ? 次,? 条评论

启用 cacheComponents 新标志时(默认不缓存),以下绝大部分内容概念已被抛弃

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

名称是什么在哪里目的生命周期
Request Memoization函数的返回值服务器在 React 组件树中重用数据每个请求的生命周期
Data Cache数据服务器跨用户请求和部署存储数据持久性(可重新验证)
Full Route CacheHTML 和 RSC 有效载荷服务器降低渲染成本并提高性能持久性(可重新验证)
Router CacheRSC 有效载荷客户减少导航时的服务器请求用户会话或基于时间

默认情况下,Next.js 会尽可能多地缓存,以提高性能并降低成本。这意味着路由会被静态渲染 ,并且数据请求也会被缓存 ,除非您选择退出。

middleware 不支持获取缓存。任何在 middleware 内部执行的获取操作都将被取消缓存。

Request Memoization

Next.js 扩展了 fetch API ,可以自动记忆具有相同 URL 和选项的请求。这意味着你可以在 React 组件树的多个位置针对同一数据调用 fetch 函数,而不必担心通过网络对同一数据进行多次请求所带来的性能影响。

  • 在渲染路线时,第一次调用特定请求时,其结果将不会在内存中,并且将是缓存 MISS
  • 因此,将执行该函数,并从外部源获取数据,并将结果存储在内存中
  • 同一次渲染过程中该请求的后续函数调用将会是一次缓存命中 HIT,数据将从内存中返回,而无需执行该函数
  • 一旦路线被渲染并且渲染过程完成,内存就会被“重置”,并且所有请求记忆条目都会被清除
温馨提示
  • 请求记忆化是 React 的功能,而非 Next.js 的功能。此处介绍它是为了展示它如何与其他缓存机制交互
  • 记忆化仅适用于 fetch 请求中的 GET 方法
  • 记忆化仅适用于 React 组件树,这意味着:
    • 它适用于 generateMetadata 、 generateStaticParams 、Layouts、Pages 和其他服务器组件中的 fetch 请求
    • 它不适用于 Route Handlers 中的 fetch 请求,因为它们不是 React 组件树的一部分
  • 对于不适合使用 fetch 的情况(例如某些数据库客户端、CMS 客户端或 GraphQL 客户端),您可以使用 React cache 功能来记忆函数

生命周期

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

重新验证

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

Data Cache

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

  • 在渲染过程中第一次调用带有 'force-cache' 选项的 fetch 请求时,Next.js 会检查数据缓存中是否有缓存的响应
  • 如果找到缓存的响应,则立即返回并记忆
  • 如果未找到缓存的响应,则向数据源发出请求,将结果存储在数据缓存中并进行记忆
  • 对于未缓存的数据(例如,未定义 cache 选项或使用 { cache: 'no-store' } ),结果始终从数据源获取并记忆
  • 无论数据是否缓存,请求总是会被记住,以避免在 React 渲染过程中对相同数据进行重复请求
数据缓存和请求记忆之间的差异

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

生命周期

除非您重新验证或选择退出,否则数据缓存将在传入请求和部署中持续存在

重新验证

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

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

    • 要按时间间隔重新验证数据,您可以使用 fetch 的 next.revalidate 选项来设置资源的缓存寿命(以秒为单位)

      fetch('https://...', { next: { revalidate: 3600 } })
    • 或者,您可以使用路由段配置选项来配置段中的所有 fetch 请求,或者在您无法使用 fetch 情况下使用

      const dynamic = 'force-dynamic'
      const fetchCache = 'default-no-store'
      • 第一次调用带有 revalidate 的获取请求时,数据将从外部数据源获取并存储在数据缓存中
      • 在指定时间范围内(例如 60 秒)调用的任何请求都将返回缓存数据
      • 在该时间范围之后,下一个请求仍将返回缓存的(现在已过时的)数据
        • Next.js 将在后台触发数据的重新验证
        • 一旦成功获取数据,Next.js 将使用新数据更新数据缓存
        • 如果后台重新验证失败,之前的数据将保持不变
  • 按需重新验证: 根据事件(例如表单提交)重新验证数据。按需重新验证可以使用基于标签 revalidateTag 或基于路径 revalidatePath 的方法一次性重新验证多组数据。当您希望确保尽快显示最新数据时(例如,当您的无头 CMS 中的内容更新时),此功能非常有用

    • 第一次调用 fetch 请求时,数据将从外部数据源获取并存储在数据缓存中
    • 当触发按需重新验证时,相应的缓存条目将从缓存中清除,这与基于时间的重新验证不同,后者将陈旧数据保留在缓存中,直到获取新数据为止
    • 下次请求时,又是缓存 MISS ,会从外部数据源中获取数据,存放到 Data Cache 中

Full Route Cache

Next.js 会在构建时自动渲染并缓存路由。这是一种优化,允许您使用缓存的路由,而不是每次请求都重新在服务器上渲染,从而提高页面加载速度

  • 服务器端 React 渲染 在服务器上,Next.js 使用 React 的 API 来编排渲染。渲染工作被拆分成多个块:通过单独的路由段和 Suspense 边界
    • React 将服务器组件渲染为一种特殊的数据格式,针对流式传输进行了优化,称为 React 服务器组件有效负载
    • Next.js 使用 React 服务器组件有效负载和客户端组件 JavaScript 指令在服务器上呈现 HTML。这意味着我们不必等待所有内容渲染完毕再缓存工作或发送响应。相反,我们可以在工作完成时流式传输响应
React 服务器组件有效负载是什么?

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

  • 服务器组件渲染结果
  • 客户端组件渲染位置的占位符以及对其 JavaScript 文件的引用
  • 从服务器组件传递到客户端组件的任何 props
  • Next.js 服务器缓存(完整路由缓存) Next.js 的默认行为是将路由的渲染结果(React 服务器组件负载和 HTML)缓存在服务器上。这适用于构建时或重新验证期间静态渲染的路由
  • 客户端上的 React Hydration 和 Reconciliation
    • HTML 用于立即显示客户端和服务器组件的快速非交互式初始预览
    • React 服务器组件有效负载用于协调客户端和渲染的服务器组件树,并更新 DOM
    • JavaScript 指令用于 水合 客户端组件并使应用程序具有交互性
  • Next.js 客户端缓存(路由器缓存) React 服务器组件的有效负载存储在客户端的路由器缓存中(一个独立的内存缓存),按各个路由段进行拆分。此路由器缓存用于存储先前访问过的路由并预取未来的路由,从而提升导航体验
  • 后续导航 在后续导航或预取过程中,Next.js 会检查 React 服务器组件有效负载是否存储在路由器缓存中。如果是,则不会向服务器发送新的请求;如果路由段不在缓存中,Next.js 将从服务器获取 React Server Components Payload,并填充客户端上的路由器缓存

静态和动态渲染

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

生命周期

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

缓存无效

  • 重新验证数据:重新验证数据缓存,将通过在服务器上重新渲染组件并缓存新的渲染输出,使路由器缓存无效
  • 重新部署:与跨部署持续存在的数据缓存不同,完整路由缓存在新部署时会被清除

退出缓存

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

  • 使用 动态 API:这将使路由从完整路由缓存中退出,并在请求时动态渲染。数据缓存仍然可以使用
  • 使用 dynamic = 'force-dynamic' 或 revalidate = 0 路由段配置选项:这将跳过完整路由缓存和数据缓存。这意味着每次向服务器发出请求时,组件都会被渲染并获取数据。由于路由器缓存是客户端缓存,因此它仍然有效
  • 退出数据缓存:如果路由包含未缓存的 fetch 请求,则该路由将退出完整路由缓存。每次收到请求时,都会获取该 fetch 请求对应的数据。其他明确启用缓存的 fetch 请求仍将缓存在数据缓存中。这允许混合缓存和非缓存数据

Router Cache

Next.js 有一个内存客户端路由器缓存,用于存储路由段的 RSC 有效负载,按布局、加载状态和页面拆分

当用户在路由之间导航时,Next.js 会缓存已访问的路由段,并预取用户可能导航到的路由。这样可以实现即时的前进/后退导航,导航之间无需重新加载整个页面,并且在共享布局中保留浏览器状态和 React 状态

  • 布局被缓存并在导航(部分渲染)时重复使用
  • 加载状态被缓存并在导航中重复使用以实现即时导航
  • 页面默认不缓存,但在浏览器前后导航时会被重复使用。您可以使用实验性的 staleTimes 配置选项来启用页面片段的缓存

生命周期

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

  • 会话:缓存在导航过程中持续存在。但是,页面刷新后缓存会被清除
  • 自动失效期:布局和加载状态的缓存会在特定时间后自动失效。失效时间取决于资源的预获取方式以及资源是否为静态生成
    • 默认预取(prefetch={null} 或未指定):动态页面不缓存,静态页面缓存 5 分钟
    • 完全预取(prefetch={true} 或 router.prefetch):静态和动态页面都需要 5 分钟

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

退出缓存

  • 在服务器操作中:
    • 通过路径 revalidatePath 或通过缓存标签 revalidateTag 按需重新验证数据
    • 使用 cookies.set 或 cookies.delete 会使路由器缓存无效,以防止使用 cookie 的路由变得陈旧(例如身份验证)
  • 调用 router.refresh 将使路由器缓存无效,并向服务器发出当前路由的新请求

上一页

fetch 封装

下一页

AutoHotkey
  • Request Memoization
  • 生命周期
  • 重新验证
  • Data Cache
  • 生命周期
  • 重新验证
  • Full Route Cache
  • 静态和动态渲染
  • 生命周期
  • 缓存无效
  • 退出缓存
  • Router Cache
  • 生命周期
  • 退出缓存