首页 / Astro 教程 / 路由缓存 Route Caching

Astro 教程

路由缓存 Route Caching

本教程共 56 篇 · 第 49 篇 · 更新于 2026-08-07 · 约 14 分钟阅读

AstroAstro 教程路由缓存Route Caching按需渲染缓存失效routeRulesstale-while-revalidate

本节目标:掌握 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 按规则合并:

  • 标量值(maxAgeswretag):后写的覆盖先写的。
  • 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.enabledfalsecache.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 页面的一层「减速带」,既保性能又保新鲜。