路由缓存 Route Caching
本教程共 56 篇 · 第 49 篇 · 更新于 2026-08-07 · 约 14 分钟阅读
本节目标:掌握 Astro 7 中已稳定的路由缓存能力,学会在按需渲染场景下用统一 API 给页面和端点加缓存、打标签、按标签或路径精准失效,从而在不堆服务器成本的前提下提升响应速度。
缓存这东西,听起来像后端专有,但 Astro 把它做成了「和平台无关」的一套 API。这一章是 v7 的重点新特性,社区旧文基本没覆盖,我们按官方文档讲清楚。
先说前提:缓存只对按需渲染生效
路由缓存(Route Caching)缓存的是「按需渲染」(on-demand rendering)页面和端点的响应。它的前提是:你已经装了适配器,并启用了按需渲染(output: 'server' 或 'hybrid',或者对单个页面设 prerender = false)。
为什么?因为预渲染(prerendered)的页面在构建时就成了静态文件,直接由 CDN/静态服务器返回,本就不走这套缓存逻辑。只有每次请求都现渲染的页面,才需要缓存来挡掉重复计算。
如果你还没接触按需渲染,建议先读第 36 章。这里只要记住一句话:静态页天生快,缓存是给「现做现卖」的 SSR 页面提速用的。
配置缓存提供商
要用路由缓存,先在 astro.config.mjs 里指定一个缓存提供商(provider)。Astro 7 内置了内存缓存 memoryCache(),常配合 Node 适配器用:
// astro.config.mjs
import { defineConfig, memoryCache } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
adapter: node({ mode: 'standalone' }),
cache: {
provider: memoryCache(),
},
});
memoryCache() 把缓存存在服务器进程内存里,适合单实例部署或本地验证。不同平台/适配器未来会有自己的边缘缓存提供商。
补充说明(延伸,不展开):Astro 7.2 里还有实验性的 CDN 缓存提供商(Netlify/Vercel/Cloudflare,私有测试阶段),能把缓存指令下推到主机边缘网络,命中时由 CDN 直接返回、不触发服务器函数。比如 import { cacheNetlify } from '@astrojs/netlify/cache'; cache: { provider: cacheNetlify() }。这些还是实验特性,生产使用前请以官方文档为准。
如果你是从 Astro 6 的实验版迁移过来的:只需把 experimental.cache / experimental.routeRules 从 experimental 块挪到配置顶层,API 不变。
在页面里设置缓存
最常用的是在 .astro 页面里用 Astro.cache.set({...})。先看一个例子:
---
export const prerender = false; // server 模式下可省略
Astro.cache.set({
maxAge: 120, // 新鲜期 120 秒(2 分钟)
swr: 60, // 之后再 60 秒内返回陈旧内容并后台重验
tags: ['home'],// 打标签,便于精准失效
});
---
三个核心参数:
maxAge:新鲜窗口,单位秒。这段时间内缓存被视为「新鲜」,直接返回,不再重新渲染。swr:stale-while-revalidate 的秒数。新鲜期过后,它允许在一段时间内先返回旧内容(访客无感),同时后台悄悄重新生成新版本。这是性能和新鲜度的折中。tags:标签数组。给这份缓存起名字,后面失效时按标签批量清除。
在 API 路由和中间件里设置
端点(第 34 章讲过,即 src/pages/ 下的 .ts 文件)和中间件(第 37 章)里没有 Astro 全局对象,要用请求上下文的 context.cache:
// src/pages/api/data.ts
export function GET(context) {
context.cache.set({ maxAge: 300, tags: ['api', 'data'] });
return Response.json({ ok: true });
}
中间件也能设缓存——它可以在请求到达页面之前就统一打上缓存指令。这让「缓存策略」可以分散在中间件、布局、内容加载器、页面代码各处,各自贡献一部分。
退出缓存:个性化内容不缓存
不是所有页面都该缓存。比如带登录用户信息的页面,缓存了就会把 A 用户的内容发给 B 用户。用 cache.set(false) 显式退出:
---
if (isPersonalized) {
Astro.cache.set(false);
}
---
这表示「这次请求不要缓存」。适合个性化、含敏感数据、或实时性极高的响应。
读取当前缓存选项
想调试或做条件判断,可用 cache.options 拿到当前累积的缓存选项:
const { maxAge, swr, tags } = context.cache.options;
注意这是「累积值」——同一请求内多次 cache.set() 会合并(见下文合并规则),options 反映的是合并后的结果。
缓存合并规则
同一个请求里,中间件、布局、页面可能各自调了 cache.set(),Astro 按规则合并:
- 标量值(
maxAge、swr、etag):后写的覆盖先写的。 lastModified:取最新的日期。tags:跨所有调用累积(不会互相覆盖,只会越加越多)。
所以你不必把所有缓存逻辑堆在一个地方。布局里设个默认值,页面里再针对具体数据追加 tags,是常见写法。
失效:内容变了要主动清缓存
缓存最怕「该更新的没更新」。Astro 提供 cache.invalidate(),按标签或路径清除。典型场景是 CMS 推送 webhook 通知「某内容改了」,你主动清掉对应缓存:
// src/pages/api/revalidate.ts
import type { APIRoute } from 'astro';
export const POST: APIRoute = async ({ request, cache }) => {
const { slug } = await request.json();
await cache.invalidate({ tags: ['products'] }); // 按标签清除
await cache.invalidate({ tags: [`products:${slug}`] });
await cache.invalidate({ path: `/products/${slug}` }); // 按路径精确清除
return new Response('Revalidated');
};
两种失效方式:
- 按标签:清除所有包含任一所给标签的缓存条目。适合「这一类产品全变了」的场景。
- 按路径:只精确匹配给定路径,不支持通配符(不能写
/products/*)。要匹配一组路径,得用下面的routeRules或枚举 tags。
用 routeRules 批量声明缓存
如果每个页面都手写 cache.set() 太散,可以在配置里用 routeRules 按路由模式统一声明,把缓存逻辑移出业务代码:
// astro.config.mjs
export default defineConfig({
cache: { provider: memoryCache() },
routeRules: {
'/api/[...path]': { swr: 600 },
'/products/[...slug]': { maxAge: 3600, tags: ['products'] },
'/blog/[...slug]': { maxAge: 300, swr: 60 },
},
});
它支持静态路径、动态参数 [id]、rest 参数 [...path],匹配优先级和文件路由一致(更具体的优先)。两点注意:
- 不支持
*通配符,想匹配一组路由请用[...rest]参数。 - 页面里的
cache.set()会和 routeRules 合并,页面可以覆盖或扩展默认值。
检查缓存是否启用
写代码时最好先判断缓存到底有没有生效,免得在无提供商或开发模式下误用 API:
---
if (Astro.cache.enabled) {
const tags = await getProductTags(Astro.params.id);
Astro.cache.set({ maxAge: 3600, tags });
}
---
cache.enabled 返回「是否配置了提供商且当前生效」。未启用时,cache.set() / cache.tags / cache.options 会告警,cache.invalidate() 会直接报错。所以拿不准就先用 cache.enabled 判断。
开发模式不发生真实缓存
开发时(astro dev)缓存 API 是可用的——你的路由代码不用写条件判断也能跑。但这里不会发生真实缓存:cache.enabled 为 false,cache.set() 和 cache.invalidate() 都是空操作(no-op)。
本地想真实验证缓存行为,用 astro build 构建后再 astro preview 启动预览服务器来测。这是排查「为什么没缓存」的第一步。
与 live content collections 集成
路由缓存还能直接对接 live content collections(实时内容集合)。live 加载器(loader)能返回 cacheHint(缓存提示,含 tags 和 lastModified),你直接喂给缓存即可:
---
import { getLiveEntry } from 'astro:content';
const { entry, error, cacheHint } = await getLiveEntry('products', Astro.params.id);
if (error) return Astro.redirect('/404');
if (cacheHint) Astro.cache.set(cacheHint); // 直接用加载器给的提示
Astro.cache.set({ maxAge: 300 });
---
也可以直接 Astro.cache.set(entry),Astro 会自动从条目里提取它的 cacheHint。这把「数据多久变一次」和「页面缓存多久」连成了一条线,省去你手动对齐时间。
一句延伸:增量静态构建
最后提一个 Astro 7.2 的实验性能力——增量静态构建(incremental static builds)。开启 experimental.incrementalBuild: true 后,预渲染页面在 getStaticPaths() 里返回 cacheKey(比如文章的摘要 digest),没变化的页就跳过重新生成。这属于静态构建层面的优化,和本章讲的「按需渲染路由缓存」是两个不同层面,点到即止,不展开。
小结
路由缓存的脉络很清晰:装适配器开按需渲染 → 配 cache.provider → 用 cache.set / routeRules 设 maxAge/swr/tags → 内容更新时用 cache.invalidate 按标签或路径失效 → 不确定就查 cache.enabled。它是 Astro 给 SSR 页面的一层「减速带」,既保性能又保新鲜。