缓存

Nitro 提供了一个构建于存储层之上的缓存系统,由 ocache 提供支持。

缓存可以存储开销较大的操作结果(渲染后的响应、上游 API 调用、复杂计算),并在之后再次提供这些结果,而无需重新执行操作。Nitro 提供了三种使用方式:

  • 缓存处理器:缓存事件处理器的完整响应。
  • 缓存函数:缓存任意函数的结果,并在多个处理器之间复用。
  • 路由规则:通过配置为路由模式启用缓存,而无需修改处理器代码。

#缓存处理器

要缓存事件处理器,请使用 defineCachedHandler 方法。

它的工作方式类似于 defineHandler,但多了一个用于传入缓存选项的第二个参数。

routes/cached.ts
import { defineCachedHandler } from "nitro/cache";

export default defineCachedHandler((event) => {
  return "我会被缓存一小时";
}, { maxAge: 60 * 60 });

在此示例中,响应会缓存 1 小时。缓存过期后,下一个请求会等待处理器解析出新的值,然后再返回响应。如果你希望在后台重新验证时立即提供过期响应,请设置 swr: true(请参阅 SWR 行为)。

有关可用选项的更多详情,请参阅选项部分。

Important

maxAge 默认为 1 秒。对于任何确实希望缓存的内容,请务必显式设置此选项。

#处理器可以看到的内容

对于可缓存的请求,处理器接收到的请求数据,恰好就是缓存键所涵盖的数据。在处理器运行前,其他所有内容都会被移除,因此处理器无法根据缓存键无法区分的值来渲染输出,并将其存储在共享键下:

  • 请求头会被剥离,除非列在 varies 中。这包括 if-none-match / if-modified-since(Nitro 会自行处理条件请求)以及 traceparent 或 x-request-id 等追踪头。
  • 查询参数会被剥离,除非列在 allowQuery 中。默认情况下,/page、/page?a=1 和 /page?utm=x 都会共用一个条目,并且处理器内部的 url.searchParams 为空。
  • Cookie会被剥离,除非列在 allowCookies 中,并且每个缓存响应中的 set-cookie 都会被移除。
  • authorization / proxy-authorization 会被剥离,除非设置了 allowAuthorization。
  • host 是唯一会被重写而非移除的请求头:它携带已解析请求源的主机名,该主机名属于缓存键的一部分。因此,为多个主机名提供服务的处理器会为每个主机名生成一个条目。

对于必须原样到达处理器的请求,请使用 shouldBypassCache。绕过缓存的请求会保留其请求头、Cookie、查询字符串和请求体,且其响应不会被存储。

Read more in ocache.unjs.io/docs/handler#headers-the-handler-cant-see.

#自动 HTTP 头

使用 defineCachedHandler 时,Nitro 会自动管理缓存响应上的 HTTP 缓存头:

  • etag:如果处理器尚未设置,则根据响应体哈希生成弱 ETag(W/"...")。
  • cache-control:根据实际应用于条目的有效期生成,除非处理器已设置此项(请参阅下表)。
  • vary:varies 中的每个名称都会合并到响应的 Vary 中;使用 allowCookies 时也会加入 Cookie。allowQuery 不会添加任何内容,因为查询参数已经包含在 URL 中。
  • x-cache:类似 CDN 的缓存状态头(HIT、STALE、REVALIDATED 或 MISS)。可通过 cacheStatusHeader 选项自定义。
选项合成的 cache-control
{ maxAge: 60 }max-age=60
{ maxAge: 60, swr: true }max-age=60, s-maxage=60
{ maxAge: 60, swr: true, staleMaxAge: 600 }max-age=60, s-maxage=60, stale-while-revalidate=600

只有设置了 staleMaxAge 时,才会声明 stale-while-revalidate,因为该指令需要一个以秒为单位的数值。设置 swr: true 而未设置 staleMaxAge 时,过期窗口没有限制,因此下游缓存会在 max-age 之后重新验证,而 Nitro 会使用其过期副本响应。

要仅在服务器端缓存,而不向客户端和 CDN 声明 cache-control,请设置 sendCacheControl: false。

Note

永远不会生成 last-modified。 条目填充的时间并不等于内容更改的时间,因此当处理器知道实际修改时间时,应自行设置此请求头。同样,处理器设置的 cache-control 也永远不会被覆盖。

#条件请求(304 Not Modified)

缓存处理器会自动支持条件请求。当客户端发送的 if-none-match 与缓存的 etag 匹配,或者 if-modified-since 的时间等于或晚于处理器设置的 last-modified 时,Nitro 会返回不含响应体的 304 Not Modified 响应。如果两者都存在,则以 if-none-match 为准。

请求验证器会在请求被缩减范围之前捕获,因此处理器永远不会看到它们,也无法自行处理条件请求。

#可缓存的请求

只有 GET 和 HEAD 请求会被缓存。所有其他 HTTP 方法(POST、PUT、DELETE 等)都会自动绕过缓存并直接调用处理器;携带 Range 请求头的请求也会如此。

GET 和 HEAD 会使用单独的条目进行缓存,因为 HEAD 响应不包含响应体。即使使用自定义 getKey,方法仍然是缓存键的一部分。

#请求去重

当缓存正在解析时,如果有多个并发请求命中相同的缓存键,只会运行一次处理器调用。所有并发请求都会等待并共享相同的结果。

共享解析受 maxResolveTime 限制(默认 30 秒)。超时后,所有等待者都会因 TimeoutError 而被拒绝,正在刷新的条目会被移除,并且该键可以再次使用。此外,处理器还会通过 event.req.signal 收到此截止时间,因此你可以将其传递给上游 fetch 调用。

#缓存函数

你也可以使用 defineCachedFunction 缓存函数。这适用于缓存非事件处理器函数的结果,但该函数是事件处理器的一部分,并且需要在多个处理器中复用的情况。

例如,你可能想将 API 调用的结果缓存一小时:

routes/api/stars/[...repo\
import { defineCachedFunction } from "nitro/cache";
import { defineHandler, type H3Event } from "nitro";

export default defineHandler(async (event) => {
  const { repo } = event.context.params;
  const stars = await cachedGHStars(repo).catch(() => 0)

  return { repo, stars }
});

const cachedGHStars = defineCachedFunction(async (repo: string) => {
  const data = await fetch(`https://api.github.com/repos/${repo}`).then(res => res.json());

  return data.stargazers_count;
}, {
  maxAge: 60 * 60,
  name: "ghStars",
  getKey: (repo: string) => repo
});

星标数会使用缓存存储(默认为内存)进行缓存。缓存键由组、名称和 getKey 的结果生成,而 value 则是星标数。

{"expires":1677851092249,"value":43991,"mtime":1677847492540,"integrity":"ZUHcsxCWEH"}

Important

由于缓存数据会被序列化为 JSON,因此缓存函数不能返回无法序列化的内容,例如 Symbols、Maps、Sets……

对于本身就是字节的值(Uint8Array、ArrayBuffer 或任何类型化数组),无需使用钩子即可处理,并且始终会作为 Uint8Array 返回。其存储方式取决于后端:声明了 binary 的后端会原样保存字节,而执行序列化的后端(包括 Nitro 的默认缓存存储)会将其存储为 base64。对象中嵌套的字节不会采用此处理方式;对于这类数据结构,请使用 serialize/transform。

Note

如果你使用 edge workers 托管应用,应遵循以下说明。

在 edge workers 中,实例会在每个请求完成后销毁。Nitro 会自动使用 event.waitUntil,以便在响应发送给客户端的同时,实例仍能保持运行并更新缓存。

为确保缓存函数在 edge workers 中按预期工作,应始终将 event 作为使用 defineCachedFunction 定义的函数的第一个参数传入。

routes/api/stars/[...repo\
import { defineCachedFunction } from "nitro/cache";
import { defineHandler, type H3Event } from "nitro";

export default defineHandler(async (event) => {
  const { repo } = event.context.params;
  const stars = await cachedGHStars(event, repo).catch(() => 0)

  return { repo, stars }
});

const cachedGHStars = defineCachedFunction(async (event: H3Event, repo: string) => {
  const data = await fetch(`https://api.github.com/repos/${repo}`).then(res => res.json());

  return data.stargazers_count;
}, {
  maxAge: 60 * 60,
  name: "ghStars",
  getKey: (event: H3Event, repo: string) => repo
});

这样,函数就能在更新缓存时保持实例运行,同时不会拖慢返回给客户端的响应。

对于请求不携带 waitUntil 的平台,请改用 waitUntil 选项传递平台钩子。

#选项

defineCachedHandler 和 defineCachedFunction 函数接受以下选项:

#通用选项

以下选项可用于 defineCachedHandler 和 defineCachedFunction:

用于缓存键的存储基路径(前缀),通常与存储挂载点相匹配。 :br 默认为 /cache(存储在 cache: 前缀下)。如果提供数组,读取时会按顺序尝试每个基路径,写入时则会写入所有基路径(多层缓存)。

如果未提供,则根据函数名称猜测;否则回退为 anon_<hash>(根据函数代码计算)。

处理器默认为 'nitro/handlers',函数默认为 'nitro/functions'。

接受与原始函数相同参数并返回缓存键(String)的函数。 :br 如果未提供,则会使用内置哈希函数,根据函数参数生成键。对于缓存处理器,键由请求源、路径和方法生成。 :br :br 自定义 getKey 会替代生成的键,但不会替代请求范围缩减:处理器可以看到哪些内容,仍由 allowQuery、allowCookies 和 varies 决定。

更改时会使缓存失效的值。 :br 默认情况下,它根据函数代码和选项计算,因此更改其中任一项都会静默忽略旧条目,而不是提供旧条目。

缓存有效的最长时间,以秒为单位。 :br 默认为 1(秒)。显式设置为 0 会禁用该处理器或函数的缓存;省略或设为 undefined 时,则回退到默认值。

在 maxAge 过期后,后台重新验证期间仍可提供过期值的最长时间,以秒为单位。仅在启用 swr 时适用。 :br 设置为 0 时,永远不会提供过期值(等同于禁用 swr)。未设置时,过期值可以无限期提供,并且不会写入存储 TTL,因此条目会一直保留,直到后端将其清除。

启用 stale-while-revalidate 行为,在异步重新验证的同时提供过期的缓存值。 :br 启用后,过期的缓存值会立即返回,同时在后台进行重新验证。禁用后,调用方会在前台等待值重新验证完成。 :br 默认为 false。请参阅 SWR 行为。

根据解析后的值为每个条目推导缓存有效期,并覆盖该条目的静态 maxAge / staleMaxAge 选项。返回以秒为单位的数值(maxAge 的简写),或返回可同时覆盖 staleMaxAge 的对象。 :br 适用于携带自身过期时间的值,例如访问令牌:getMaxAge: (entry) => entry.value.expires_in。解析后的值 <= 0 时,会禁用该条目的缓存。对于处理器,entry.value 是 Response,合成的 cache-control 会遵循返回的有效期。

一次共享解析的截止时间,以秒为单位。默认为 30。 :br 超时后,所有等待的调用方都会因 TimeoutError 而被拒绝,并且正在刷新的条目会被移除。设置为 Infinity 或 0 可禁用此限制;对于确实运行缓慢的解析器,请调高此值。

覆盖此处理器或函数所使用的后端。 :br 默认为 Nitro 的缓存存储,它是 Nitro 存储层中 cache: 前缀的适配器。

将后台工作(缓存写入、SWR 刷新、清除操作)交给宿主运行时处理。 :br 优先级高于 event.req.waitUntil。仅在请求不携带此方法的无服务器平台上需要使用。

返回 boolean 的函数,用于使当前缓存失效并创建新缓存。

返回 boolean 的函数,用于绕过当前缓存但不使现有条目失效。 :br 对于处理器,绕过缓存的请求会以未更改的状态到达处理器(包括凭据、Cookie 和完整查询字符串),其响应既不会被存储,也不会带有缓存头。

缓存函数抛出错误时调用的自定义错误处理器。 :br 默认情况下,错误会记录到控制台,并由 Nitro 错误处理器捕获。

#仅适用于处理器的选项

以下选项仅适用于 defineCachedHandler:

为 true 时,跳过完整响应缓存,仅处理条件请求头(if-none-match、if-modified-since),用于返回 304 Not Modified 响应。每个请求都会调用处理器,并以处理器自身设置的 etag / last-modified 作为条件,因此处理器必须自行设置这些值。此模式下不会生成 x-cache 请求头。

作为缓存键变化依据的请求头名称数组。此处列出的请求头在缓存解析期间会保留在请求中,并纳入缓存键,使缓存按请求头值的不同组合进行区分。它们也会合并到响应的 Vary 请求头中。 :br :br 未列在 varies 中的请求头会在调用处理器之前从请求中剥离,以确保缓存命中一致。 :br :br 如果响应自身的 Vary 列出了键中未包含的请求头,则该响应会返回,但不会被存储,因此请确保两个列表保持同步。 :br :br 多租户配置现在无需在此处添加 ['host']:请求源已经是生成的键的一部分。只有当处理器在代理后会读取 x-forwarded-host 时,才需将其添加进来。

允许作为缓存键变化依据的查询参数名称白名单。默认情况下,任何查询参数都不会影响缓存键,也不会传递给处理器,因此 /page?a=1 和 /page?utm=x 会共用一个条目,并且处理器内部的 url.searchParams 为空。 :br :br 设置数组可指定允许的名称(区分大小写,不区分顺序);设置为 true 则会根据完整查询字符串生成键,适用于参数无法预先确定的代理和搜索端点。

参与缓存的 Cookie 名称白名单。列出的 Cookie 会影响缓存键,并保留在处理器所见的 cookie 请求头中。 :br :br 默认情况下不允许任何 Cookie:处理器运行前会剥离 cookie 请求头,并且响应中的任何 set-cookie 请求头都会在缓存或返回前被丢弃,因此每个用户专属的 Cookie(例如会话 ID)永远不会通过缓存泄漏给其他用户。只将其值可安全地在所有命中同一缓存键的用户之间共享的 Cookie 加入白名单(例如 theme 或 locale 偏好),切勿加入用户专属的机密信息。 :br :br 设置此选项会生成 Vary: Cookie,这可能会降低 CDN 和共享代理的命中率。可以考虑改用 allowQuery 将选择放入 URL,或与 sendCacheControl: false 配合使用。

允许 authorization 和 proxy-authorization 到达处理器,并使缓存键随其变化。默认为 false,此时可缓存请求中的这两个请求头都会被剥离。 :br :br 发送相同凭据值的所有客户端会共用一个条目。如果响应绝不能共享,请改用 shouldBypassCache。

是否自动设置 cache-control 响应头。默认为 true。 :br 设置为 false 可仅在服务器端缓存:响应仍会存储并从缓存提供,但不会向客户端和 CDN 声明 cache-control。此设置不会生成 no-store,因此下游缓存仍可能根据自身启发式规则存储响应。

添加缓存状态响应头(X-Cache: HIT | STALE | REVALIDATED | MISS)。默认为 true。传入字符串可使用自定义请求头名称,传入 false 可禁用。

直接流式传输用于填充缓存条目的响应,而不是先进行缓冲。默认为 false。 :br 这可以改善流式渲染的首字节时间,但会导致无法生成合成的 etag(摘要需要完整的响应体),并且会影响响应体中途出错时的恢复。任何不完整内容都不会被存储,后续请求仍会像往常一样使用已存储的条目。

可缓冲并用于存储的最大响应体大小,以字节为单位。更大的响应会像绕过缓存的请求一样直接流式传输,不会被缓存。 :br Nitro 的默认存储未声明单个条目的大小上限,因此默认没有限制。对于代理大小不受你控制的上游响应的任何路由,请设置此选项。

在内置检查的基础上,额外判断响应是否可缓存(内置检查始终适用,且只能进一步收窄缓存范围)。返回 false 可跳过响应缓存;响应仍会返回给调用方,只是不予存储。 :br 此函数在读取和写入时都会运行,可以是异步函数,并且会以安全失败的方式处理:钩子抛出错误时会将响应视为“不可缓存”,并交由 onError 处理。

#仅适用于函数的选项

以下选项仅适用于 defineCachedFunction:

在返回缓存条目之前对其进行转换。返回值会替代缓存值。 :br 条目还会携带每次调用对应的 entry.status("hit"、"stale"、"revalidated" 或 "miss"),用于说明值的提供方式,适用于指标统计或条件逻辑。

在将条目写入存储之前,准备好已解析的值(对应于 transform 的写入侧操作)。适用于函数返回无法原样存储的内容(例如流或类实例)的情况:serialize 会在写入时转换数据,而 transform 会在读取时恢复数据。 :br 每次解析只会运行一次,且去重调用会共享该结果,因此可以安全地读取流等只能使用一次的数据源。

验证缓存条目。返回 false(或解析为 false 的 Promise)会将条目视为无效并触发重新解析。第二个参数包含当前调用的 args,因此可以根据这些参数验证条目。

#SWR 行为

stale-while-revalidate(SWR)模式需要通过 swr 选项显式启用(默认禁用)。了解它与其他选项的交互方式:

swrmaxAge行为
false(默认)3600缓存 1 小时;过期时等待新值
true3600缓存 1 小时;重新验证时提供过期值
true3600,且设置 staleMaxAge: 600缓存 1 小时;重新验证时最多再提供 10 分钟的过期值
true3600,且设置 staleMaxAge: 0缓存 1 小时;永远不提供过期值(等同于 swr: false)

启用 swr 且存在已过期的缓存值时:

立即将过期的缓存值返回给客户端。
在后台调用函数或处理器以刷新缓存。
在 edge workers 上,使用 event.waitUntil 保持后台刷新继续运行。

禁用 swr(默认)且缓存值已过期时:

客户端会等待函数或处理器解析出新值。
调用返回前会替换该条目(状态为 REVALIDATED)。

Tip

当允许数据略微过期时(例如内容页面、列表、第三方 API 镜像),请启用 swr,这样即使缓存过期,响应时间也能保持稳定。对于调用方绝不能收到过期数据的情况(例如访问令牌或逐请求授权检查),请保持禁用。

Warning

设置 swr: true 且未设置 staleMaxAge 时,条目不会携带存储 TTL:最后一个有效值会一直提供,直到后端将其清除。这正是 ISR 模式,但也意味着缓存增长受后端容量而非时间限制。请设置 staleMaxAge,以便最终清理条目。

#缓存键

使用 defineCachedFunction 或 defineCachedHandler 函数时,缓存键会按照以下模式生成:

`${options.base}:${options.group}:${options.name}:${options.getKey(...args)}.json`

group 和 name 在进入键之前会经过转义:移除 [A-Za-z0-9_] 之外的字符;如果值因此发生变化,则会附加原始值的哈希。这就是为什么 Nitro 的 nitro/functions 组会显示为 nitrofunctions.<hash>,并且不会与 : 键结构混淆。getKey 的结果是最后一个片段,并且会按原样存储。

例如,以下函数:

import { defineCachedFunction } from "nitro/cache";

const getAccessToken = defineCachedFunction(() => {
  return String(Date.now())
}, {
  maxAge: 10,
  name: "getAccessToken",
  getKey: () => "default"
});

将存储在类似以下的键下:

cache:nitrofunctions.3aFYDY_G88ZLdElv-hEB0r8snSViMYJ1NbRtlNP9o_0:getAccessToken:default.json

默认的 base 为 /cache,存储层会将其规范化为 cache: 前缀(与处理键中其他 / 的方式相同)。

Note

对于缓存处理器,生成的键涵盖请求的源(协议、主机和端口)、URL 路径和方法,以及 varies、allowQuery 和 allowCookies 对应值的哈希。GET 和 HEAD 会分别存储。

Important

如果未提供 name,则会回退为函数源码的哈希;这种方式无法区分由工厂或循环生成的处理器或函数:它们的源码相同,但闭包变量不同,因此会共享同一个键并相互覆盖条目。对于这类情况,请务必显式传入 name(或 getKey)。

Note

如果未提供 getKey,参数会被哈希。不透明值(Blob、ReadableStream、Promise、Request、WeakMap)只会贡献其类型,因此两个不同的值也会共用一个条目;嵌套深度超过 128 层的参数会抛出 RangeError。出现上述任一情况时,请传入一个只读取结果所依赖字段的 getKey。

#哪些内容会被缓存

存储响应之前,会进行以下检查。未通过任一检查的响应仍会返回给调用方,只是永远不会被存储或从缓存提供:

检查原因
状态不是 200、203、301 或 308只有这些状态对应完整、可复用的表示。错误、204/304、206 范围响应和临时重定向都会被排除。
处理器通过 cache-control 选择退出包括 no-store、private、no-cache,或共享有效期为零(如果存在 s-maxage 则使用它,否则使用 max-age)。
Vary: *除 no-store 之外最强的“禁止共享”信号。
Vary 中指定了缓存键之外的请求头存储此响应会将某个变体重放给所有客户端。请将该请求头添加到 varies 中。
缺少响应体没有可供重放的内容。空的(零字节)200 响应是允许的,并会正常缓存。
etag 或 last-modified 等于字符串字面值 "undefined"这是将缺失值转换为字符串后通常产生的结果,会破坏条件请求。

这些检查始终适用。使用 shouldCache 可以进一步收窄缓存范围。内置检查拒绝的响应也不会获得合成的 cache-control,因此前置 CDN 无法继续缓存错误;而被你自己的 shouldCache 拒绝的响应仍会获得该请求头。

Note

cache-control: must-revalidate 不会选择退出缓存。响应会被存储,并在新鲜时提供,但过期后永远不会提供,因此仅对该响应禁用 SWR。

Read more in ocache.unjs.io/docs/cache-control.

#缓存失效

可以在运行时以编程方式使缓存条目失效(例如,在底层数据更改时通过 webhook 触发),而无需等待 maxAge 过期。

#.invalidate() 和 .expire() 方法

通过 defineCachedFunction 创建的每个函数都会公开按需重新验证方法:

  • .invalidate(...args):完全移除缓存条目。下次调用时会重新调用函数,并等待新值。
  • .expire(...args):将缓存条目标记为过期,但不移除它。启用 swr 时,仍会提供过期值,而下次调用会触发后台刷新;未启用 SWR 时,下次调用会在返回前重新解析。
  • .resolveKeys(...args):解析条目所缓存到的存储键(每个 base 前缀对应一个)。

参数会传递给 getKey,以生成缓存键。

import { defineCachedFunction } from "nitro/cache";

const cachedGHStars = defineCachedFunction(async (repo: string) => {
  const data = await fetch(`https://api.github.com/repos/${repo}`).then(res => res.json());
  return data.stargazers_count;
}, {
  maxAge: 60 * 60,
  name: "ghStars",
  getKey: (repo: string) => repo,
});

await cachedGHStars("unjs/nitro"); // populates the cache
await cachedGHStars.expire("unjs/nitro"); // marks the entry as stale
await cachedGHStars.invalidate("unjs/nitro"); // removes the entry
await cachedGHStars("unjs/nitro"); // re-invokes the function

如果没有缓存条目与给定参数匹配,.invalidate() 和 .expire() 会正常完成且不报错,并且不会更改存储。两者也会取消该键上正在进行的解析,因此清除前的值不会在清除后写入。

Note

Nitro 的 defineCachedHandler 目前会返回普通事件处理器,并且不会公开这些方法,因此无法按需使缓存路由失效。如果需要此功能,请将开销较大的操作移入由普通 defineHandler 调用的 defineCachedFunction,然后使该函数失效。

#缓存存储

缓存条目使用存储层存储在 cache: 前缀下(该前缀源自默认的 base: "/cache" 选项)。

默认情况下,没有为其配置专用挂载点:条目存储在根存储中,该存储使用内存驱动程序,并且在开发和生产环境中不会跨重启持久化。

要使用持久化后端,请通过 kv 选项设置 cache 挂载点:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  kv: {
    cache: {
      driver: 'redis',
      /* redis 连接器选项 */
    }
  }
})

在开发环境中,你也可以使用 $development 配置键覆盖缓存挂载点:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  kv: {
    cache: {
      // 生产环境缓存存储
    },
  },
  $development: {
    kv: {
      cache: {
        // development cache storage
      }
    }
  }
})

若要在预渲染时也覆盖它,请同时在 $prerender 中设置。

Note

条目会通过 unstorage 序列化为 JSON,因此 Nitro 的默认缓存存储不会声明 binary。可解码为 UTF-8 的响应体(HTML、JSON、纯文本)无论如何都会以文本形式存储;只有不是有效 UTF-8 的响应体(图片、字体、PDF)才会进行 base64 编码,字节值的缓存函数也会如此。传入你自己的、声明了 binary 的 storage(例如基于驱动程序的 getItemRaw / setItemRaw 创建 ocache 的 createBlobStorage),即可将这些内容保留为字节,并跳过编码、解码和 4/3 倍膨胀。 :br :br 默认后端也不会声明单个条目的大小上限,因此缓存处理器会缓冲任意大小的响应体,除非你设置了 maxBodySize。

Read more in ocache.unjs.io/docs/storage.
Read more in Docs > Storage.

#使用路由规则

路由规则允许你直接通过配置,为匹配 glob 模式的所有路由启用缓存。对于应用中某个部分实施全局缓存策略,而无需修改处理器代码时,这尤其有用。

将所有博客路由缓存 1 小时:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  routeRules: {
    "/blog/**": { cache: { maxAge: 60 * 60 } },
  },
});

cache 规则接受上文记录的相同处理器选项(swr、staleMaxAge、varies、allowQuery、allowCookies、allowAuthorization、sendCacheControl、cacheStatusHeader、stream、maxBodySize、headersOnly、base、group、name、integrity、maxResolveTime)。shouldCache 等钩子选项仅适用于 defineCachedHandler。

如果要使用自定义缓存存储挂载点,可以使用 base 选项。

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  kv: {
    redis: {
      driver: "redis",
      url: "redis://localhost:6379",
    },
  },
  routeRules: {
    "/blog/**": { cache: { maxAge: 60 * 60, base: "redis" } },
  },
});

#路由规则快捷方式

你可以使用 swr 快捷方式为路由规则启用 stale-while-revalidate 缓存。swr: 3600 是 cache: { swr: true, maxAge: 3600 } 的简写。设置为 true 时,SWR 会使用默认的 maxAge 1 秒,因此建议传入明确的数值。

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  routeRules: {
    "/blog/**": { swr: 3600 },
    "/api/**": { swr: 60 },
  },
});

要显式禁用某个路由的缓存,请在更具体的模式上设置 cache: false(或 swr: false)。这也会移除从范围更广的模式继承的 cache 规则:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  routeRules: {
    "/api/**": { swr: 60 },
    "/api/realtime/**": { cache: false },
  },
});

Note

使用路由规则时,缓存处理器会使用组 'nitro/route-rules',而不是默认的 'nitro/handlers';生成的 name 会将条目限定到匹配的路由处理器、HTTP 方法、规则模式和匹配的路由。

Warning

生成的范围在每个进程中都是唯一的。对于持久化缓存存储,重启或启动第二个工作进程都会从全新的条目开始,而不会复用已存储的条目。如果路由规则缓存条目需要跨进程共享,请在规则上设置显式的 name。

Read more in Docs > Routing#route Rules.