缓存机制
Next.js 提供了多种通过缓存 (opens in a new tab)路由和网络请求来提升应用性能的方式。应用会在构建时尽可能预渲染和缓存数据,以减少响应用户请求时所需的工作量。
缓存数据通过重新验证机制进行更新,可以是定期或按需触发:
- "基于时间的重新验证 (opens in a new tab)"会在应用设置的重新验证延迟过期后更新缓存数据
- "按需重新验证 (opens in a new tab)"允许通过特定标签(使用
revalidateTag
)或指定路径(使用revalidatePath
)使缓存条目失效。在 Pages 路由器的 API 路由中也可以使用res.revalidate
@opennextjs/cloudflare
的缓存支持依赖于三个组件:
- 增量缓存:用于存储缓存数据
- 队列:用于同步和去重基于时间的重新验证
- 标签缓存:用于通过
revalidateTag
(opens in a new tab)和revalidatePath
(opens in a new tab)进行按需重新验证
您还可以启用缓存拦截功能,避免调用NextServer
从而加载页面相关的 JavaScript。这可以略微提升 ISR/SSG 路由在缓存路由上的冷启动性能。目前缓存拦截功能不支持 PPR,且默认未启用。
此外,部分组件使用Cache API (opens in a new tab)来提升这些不同组件的性能。如果您计划使用按需重新验证,还应使用缓存清除组件在页面重新验证时自动清除缓存。
适配器为open-next.config.ts
中配置的每个组件提供了多种实现方案。
本指南首先提供常见用例的建议,然后详细说明所有配置选项。
本页内容仅涉及 SSG/ISR 和数据缓存,SSR 路由无需任何缓存配置即可直接使用。
指南
小型站点使用重新验证
对于小型站点,您应采用以下实现方案:
- 增量缓存:使用 R2 存储数据
- 队列:使用基于 Durable Objects 的队列
- 标签缓存:
D1NextModeTagCache
{
"name": "<WORKER_NAME>",
// ...
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
"service": "<WORKER_NAME>",
},
],
// R2 增量缓存
"r2_buckets": [
{
"binding": "NEXT_INC_CACHE_R2_BUCKET",
"bucket_name": "<BUCKET_NAME>",
},
],
// DO 队列
"durable_objects": {
"bindings": [
{
"name": "NEXT_CACHE_DO_QUEUE",
"class_name": "DOQueueHandler",
},
],
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["DOQueueHandler"],
},
],
// D1 标签缓存 (Next 模式)
// 仅在使用按需重新验证时需要
"d1_databases": [
{
"binding": "NEXT_TAG_CACHE_D1",
"database_id": "<DATABASE_ID>",
"database_name": "<DATABASE_NAME>",
},
],
}
大型站点使用重新验证
对于大型站点,您应该使用 ShardedDOTagCache
,它比 D1NextModeTagCache
能处理更高的负载:
{
"name": "<WORKER_NAME>",
// ...
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
"service": "<WORKER_NAME>",
},
],
// R2 增量缓存
"r2_buckets": [
{
"binding": "NEXT_INC_CACHE_R2_BUCKET",
"bucket_name": "<BUCKET_NAME>",
},
],
// DO 队列和 DO 分片标签缓存
"durable_objects": {
"bindings": [
{
"name": "NEXT_CACHE_DO_QUEUE",
"class_name": "DOQueueHandler",
},
// 仅在使用按需重新验证时需要
{
"name": "NEXT_TAG_CACHE_DO_SHARDED",
"class_name": "DOShardedTagCache",
},
{
"name": "NEXT_CACHE_DO_PURGE",
"class_name": "BucketCachePurge",
},
],
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"DOQueueHandler",
// 仅在使用按需重新验证时需要
"DOShardedTagCache",
"BucketCachePurge",
],
},
],
}
SSG 静态站点
如果你的站点是静态的,你不需要队列(Queue)也不需要使用标签缓存(Tag Cache)。你可以为预渲染路由使用基于 Workers Static Assets 的只读增量缓存。
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import staticAssetsIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/static-assets-incremental-cache";
export default defineCloudflareConfig({
incrementalCache: staticAssetsIncrementalCache,
enableCacheInterception: true,
});
预发布环境
对于预发布环境,当你的站点只接收来自单个 IP 的低流量时,可以用内存队列替代 DO 队列。
参考文档
静态资源缓存
Worker 不会在静态资源前运行,因此 next.config.ts
中的 headers
选项不会应用到公共文件(public
)和不可变的构建文件(如 _next/static
)。
默认情况下,Cloudflare 的静态资源头信息 (opens in a new tab)使用 max-age=0
和 must-revalidate
,允许浏览器缓存资源但需要重新验证请求。这与Next.js 中 public
文件夹的默认行为 (opens in a new tab)相同。
Next.js 还会生成在构建之间不会改变的_不可变_文件。这些文件也将由静态资源服务提供。为了匹配Next.js 中不可变资源的默认缓存行为 (opens in a new tab),避免不必要的重新验证请求,请在 public/_headers
(opens in a new tab) 文件中添加以下头信息:
/_next/static/*
Cache-Control: public,max-age=31536000,immutable
增量静态再生 (ISR)
增量缓存有以下三种存储选项:
- R2 对象存储: 一种经济高效 (opens in a new tab)的 S3 兼容对象存储方案,适用于大量非结构化数据。数据存储在单一区域,意味着缓存交互可能较慢 - 可通过区域缓存缓解此问题。
- Workers KV: 一种高速 (opens in a new tab)键值存储,使用 Cloudflare 的分层缓存 (opens in a new tab)提高缓存命中率。当数据写入 Workers KV 时,任何 Cloudflare 节点都能读取这些缓存数据,意味着应用可以获取数据并缓存到 KV 中,后续全球任意位置的请求都能从该缓存读取。
- Workers 静态资源: 增量缓存的只读存储,从 Workers 静态资源 (opens in a new tab)提供构建时值。此缓存不支持重新验证。
1. 创建 R2 存储桶
npx wrangler@latest r2 bucket create <YOUR_BUCKET_NAME>
2. 将 R2 存储桶和服务绑定添加到 Worker
应用中 worker 使用的绑定名称为 NEXT_INC_CACHE_R2_BUCKET
。服务绑定应是对 worker 自身的引用,其中 <WORKER_NAME>
是 wrangler 配置文件中的名称。
R2 存储桶使用的前缀可通过 NEXT_INC_CACHE_R2_PREFIX
环境变量配置,默认为 incremental-cache
。
// wrangler.jsonc
{
// ...
"name": "<WORKER_NAME>",
"r2_buckets": [
{
"binding": "NEXT_INC_CACHE_R2_BUCKET",
"bucket_name": "<BUCKET_NAME>",
},
],
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
"service": "<WORKER_NAME>",
},
],
}
3. 配置缓存
在项目的 OpenNext 配置中启用 R2 缓存。
你可以选择设置区域缓存与 R2 增量缓存配合使用。这将加快缓存条目的检索速度,并减少发送到对象存储的请求数量。
区域缓存有两种模式:
short-lived
(短期):响应最多可重复使用一分钟long-lived
(长期):获取响应会重复使用直到重新验证,ISR/SSG 响应最多可重复使用 30 分钟
此外,可以通过 shouldLazilyUpdateOnCacheHit
选项启用区域缓存的延迟更新功能。当从缓存请求数据时,它会向 R2 存储桶发送后台请求以获取最新条目。此功能在 long-lived
模式下默认启用。
// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache";
// ...
// 启用区域缓存:
export default defineCloudflareConfig({
incrementalCache: withRegionalCache(r2IncrementalCache, {
mode: "long-lived",
shouldLazilyUpdateOnCacheHit: true,
}),
// ...
});
// 不启用区域缓存:
export default defineCloudflareConfig({
incrementalCache: r2IncrementalCache,
// ...
});
队列
对于使用重新验证(基于时间或按需)的项目,必须设置队列。
配置队列
在项目的 OpenNext 配置中启用缓存并设置队列。
Durable Object 队列会在需要时向页面发送重新验证请求,并支持请求去重功能。默认情况下,最多会有 10 个 Durable Object 队列实例,每个实例可以并行处理最多 5 个请求,总共支持 50 个并发 ISR 重新验证。
// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
// ...
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
export default defineCloudflareConfig({
// ...
incrementalCache: r2IncrementalCache,
queue: doQueue,
});
你还需要在 wrangler.jsonc
文件中添加一些绑定配置。
"durable_objects": {
"bindings": [
{
"name": "NEXT_CACHE_DO_QUEUE",
"class_name": "DOQueueHandler"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["DOQueueHandler"]
}
],
可以通过环境变量自定义队列行为:
- 单个 Durable Object 实例可同时处理的最大重新验证数量 (
NEXT_CACHE_DO_QUEUE_MAX_RETRIES
) - 重新验证在被视为失败前允许的最大毫秒时间 (
NEXT_CACHE_DO_QUEUE_REVALIDATION_TIMEOUT_MS
) - 重新验证失败后再次尝试的间隔时间。如果再次失败,将以指数退避方式重试,直到达到最大重试间隔 (
NEXT_CACHE_DO_QUEUE_RETRY_INTERVAL_MS
) - 对路径进行重新验证的最大尝试次数 (
NEXT_CACHE_DO_QUEUE_MAX_RETRIES
) - 禁用该 Durable Object 的 SQLite 功能。仅当你的增量缓存不具备最终一致性时才应使用 (
NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE
)
队列还有两种额外模式可供选择:direct
模式和内存队列
-
内存队列会去重请求,但仅限于单个隔离环境内。它不完全适合生产环境部署,使用需自行承担风险!
-
direct
队列模式仅用于调试目的,不建议在生产环境中使用。它仅在预览模式下有效(即wrangler dev
)对于使用 Page Router 的应用,
res.revalidate
需要提供一个名为WORKER_SELF_REFERENCE
的自引用服务绑定。
在某些情况下,你可能会遇到 Durable Object 队列对单个页面或路由的处理限制。这时可以使用 queueCache 来减少发送到队列的陈旧请求数量。这是通过在向队列发送请求前,通过 Cache API 添加并验证缓存条目来实现的。如果缓存条目已存在,则不会向队列发送请求,因为该请求会被视为已在处理中。
// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
// ...
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
import queueCache from "@opennextjs/cloudflare/overrides/queue/queue-cache";
export default defineCloudflareConfig({
// ...
incrementalCache: r2IncrementalCache,
queue: queueCache(doQueue, {
regionalCacheTtlSec: 5, // 区域缓存的 TTL,默认为 5 秒
// 是否等待队列确认请求后再返回
// 设为 false 时,缓存会立即填充,队列会在之后被调用
// 设为 true 时,缓存只会在收到队列确认后才填充
waitForQueueAck: true,
}),
});
按需重新验证的标签缓存机制
标签重新验证机制可以使用 Cloudflare D1 (opens in a new tab) 数据库或带有 SqliteStorage
的 Durable Objects (opens in a new tab) 作为后端存储,用于记录标签、路径和重新验证时间的信息。
要使用按需重新验证功能,您还应遵循 ISR 设置步骤。
如果您的应用仅使用 pages 路由,则不需要标签缓存,可以跳过此步骤。
如果您的应用不使用 revalidateTag
和 revalidatePath
,也可以跳过此步骤。
标签缓存有两种可选方案:d1NextTagCache
和 doShardedTagCache
。
选择时应基于两个关键因素:
- 预期负载:考虑您预计的流量或数据量
revalidateTag
/revalidatePath
的使用频率:评估这些功能将被使用的频繁程度
如果其中任一因素较为显著,建议选择分片数据库方案。此外,添加区域缓存可以进一步提升性能。
创建 D1 数据库和服务绑定
应用中 worker 使用的绑定名称为 NEXT_TAG_CACHE_D1
。WORKER_SELF_REFERENCE
服务绑定应指向您的 worker 自身,其中 <WORKER_NAME>
是 wrangler 配置文件中的名称。
// wrangler.jsonc
{
// ...
"d1_databases": [
{
"binding": "NEXT_TAG_CACHE_D1",
"database_id": "<DATABASE_ID>",
"database_name": "<DATABASE_NAME>",
},
],
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
"service": "<WORKER_NAME>",
},
],
}
创建重新验证记录表
D1 标签缓存需要一个 revalidations
表来跟踪按需重新验证的时间。
配置缓存
在项目的 OpenNext 配置中,启用 R2 缓存并设置队列(如上所述)。队列会在需要时向页面发送重新验证请求,但不会对请求进行去重处理。
// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache";
export default defineCloudflareConfig({
incrementalCache: r2IncrementalCache,
queue: doQueue,
tagCache: d1NextTagCache,
});
4. 在部署时初始化缓存
为了让缓存能正确初始化并包含构建时的重新验证数据,您需要在部署步骤中运行一个命令。这应该在每次部署时运行,以确保缓存能获取到每次构建的数据。
要填充远程绑定并同时创建应用的新版本 (opens in a new tab),您可以使用 deploy
或 upload
命令。同样地,preview
命令会填充本地绑定并启动 Wrangler 开发服务器。
# 填充远程绑定并立即部署 worker
opennextjs-cloudflare deploy
# 填充远程绑定并上传 worker 的新版本
opennextjs-cloudflare upload
# 填充本地绑定并启动开发服务器
opennextjs-cloudflare preview
也可以仅使用 populateCache
命令填充缓存而不执行其他步骤。
# 目标通过选项传递,可以是 `local` 或 `remote`
opennextjs-cloudflare populateCache local
自动缓存清除
缓存清除功能仅在使用自定义域名等区域(zone)配置时可用。
缓存清除组件会在页面重新验证时自动清理缓存。仅当您使用按需重新验证(On-Demand revalidation)并配合使用基于Cache API的缓存组件时才需要此功能。
该组件可以直接调用Cache API的清除函数,也可以通过中间持久化对象(durable object)路由清除请求。使用持久化对象有助于缓冲请求,避免触及API速率限制 (opens in a new tab)。
只有当您在pages路由中调用revalidateTag
、revalidatePath
或res.revalidate
时才会触发缓存清除。ISR重新验证不会触发此功能。
以下是在open-next.config.ts
中集成缓存清除组件的配置示例:
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache";
import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
import { purgeCache } from "@opennextjs/cloudflare/overrides/cache-purge/index";
export default defineCloudflareConfig({
incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived" }),
queue: doQueue,
// 仅在使用按需重新验证时需要此项
tagCache: doShardedTagCache({ baseShardSize: 12 }),
// 如果要使用PPR请禁用此项
enableCacheInterception: true,
// 也可以使用`durableObject`选项通过持久化对象进行缓存清除
cachePurge: purgeCache({ type: "direct" }),
});
如需使用持久化对象选项,需要在wrangler.jsonc
文件中添加以下绑定配置:
{
"durable_objects": {
"bindings": [
{
"name": "NEXT_CACHE_DO_PURGE",
"class_name": "BucketCachePurge",
},
],
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["BucketCachePurge"],
},
],
}
您可以通过NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS
环境变量自定义缓存清除缓冲时长,默认为5秒。其工作原理是将清除请求缓冲指定时间后一次性发送,这有助于避免触及API速率限制。