缓存响应

对于保持网站尽可能快速,缓存响应至关重要,无论是对于页面还是中间件

一个很好的默认设置是对所有响应使用stale-while-revalidate缓存。

例如,我们可以在根布局 (src/routes/layout.tsx) 中添加一个 onGet 导出,如下所示,以应用全站的良好缓存默认设置:

src/routes/layout.tsx
import { component$, Slot } from "@builder.io/qwik";
import type { RequestHandler } from "@builder.io/qwik-city";
 
export const onGet: RequestHandler = async ({ cacheControl }) => {
  cacheControl({
    // 默认情况下始终提供缓存响应,最多可以过期一周
    staleWhileRevalidate: 60 * 60 * 24 * 7,
    // 每 5 秒最多一次,在服务器上重新验证以获取此页面的新版本
    maxAge: 5,
  });
};
 
export default component$(() => {
  return (
    <main class="mx-auto max-w-[2200px] relative">
      <Slot />
    </main>
  );
});

通过上述设置,我们不仅可以获得更好的性能(页面始终从缓存中立即提供),还可以显著降低托管成本,因为我们的服务器(或边缘函数)每个页面每 5 秒最多只需要运行一次。

cacheControl

任何接受请求事件的方法都可以调用 request.cacheControl 来设置响应的缓存控制头:

src/routes/layout.tsx
import type { RequestHandler } from "@builder.io/qwik-city";
 
export const onGet: RequestHandler = async ({ cacheControl }) => {
  cacheControl({
    public: true,
    maxAge: 5,
    sMaxAge: 10,
    staleWhileRevalidate: 60 * 60 * 24 * 365,
  });
};

您也可以覆盖这个设置。例如,也许您在根目录上有一个默认的缓存设置,但是想要禁用特定页面的缓存,您可以使用嵌套布局来覆盖这个设置:

src/routes/dashboard/layout.tsx
import type { RequestHandler } from "@builder.io/qwik-city";
 
// 为 /dashboard 页面覆盖缓存设置,不缓存,因为它们对每个访问者都是唯一的
export const onGet: RequestHandler = async ({ cacheControl }) => {
  cacheControl({
    public: false,
    maxAge: 0,
    sMaxAge: 0,
    staleWhileRevalidate: 0,
  });
};
 

您可以查看完整的API 参考,了解可以传递给 request.cacheControl 的选项。

不适合缓存的情况

缓存是一个很好的默认设置,但并不适用于所有页面的所有时间。如果您的网站上有一些 URL,这些 URL 会向不同的人显示不同的内容 - 例如仅供已登录用户查看的页面,或者根据用户的位置显示不同内容的页面,您不应该使用响应中的缓存控制头对这些页面进行缓存,并且在服务器端根据访问者的情况渲染这些页面的内容。

对于对每个人都相同的高流量页面,例如主页,缓存是性能和成本方面的最佳选择。但是对于仅供已登录用户查看的页面,由于流量可能较少,禁用缓存可能是明智的。

您可以根据需要使用任何逻辑条件性地更改缓存行为:

src/routes/layout.tsx
import type { RequestHandler } from "@builder.io/qwik-city";
 
export const onGet: RequestHandler = async ({ cacheControl, url }) => {
  // 只有我们的主页是公开的,并且应该被 CDN 缓存。其他页面对每个访问者都是唯一的
  if (url.pathname === '/') {
    cacheControl({
      public: true,
      maxAge: 5,
      staleWhileRevalidate: 60 * 60 * 24 * 365,
    });
  }
};

CDN 缓存控制

为了更好地控制缓存策略,您的 CDN 可能有另一层缓存控制头。

cacheControl 便捷方法可以接收第二个参数(默认设置为 "Cache-Control")。您可以传递任何特定于您的 CDN 的字符串值,例如 "CDN-Cache-Control"、"Cloudflare-CDN-Cache-Control"、"Vercel-CDN-Cache-Control" 等。

cacheControl({
  maxAge: 5,
  staleWhileRevalidate: 60 * 60 * 24 * 365,
}, "CDN-Cache-Control");

缺失的控制

某些 CDN(例如 Vercel Edge)可能会剥离一些 "Cache-Control" 头。

在 Vercel 的文档中:

如果您设置了没有 CDN-Cache-Control 的 Cache-Control,则 Vercel Edge 网络会在将响应发送到浏览器之前从响应中剥离 s-maxage 和 stale-while-revalidate。要确定响应是否来自缓存,请检查响应中的 x-vercel-cache 头。

因此,如果您的 CDN 默认情况下剥离了一些缓存控制头(例如 Vercel Edge),并且您希望浏览器使用 "stale-while-revalidate" 或 "s-maxage",您可以添加另一个 cacheControl

src/routes/layout.tsx
import type { RequestHandler } from "@builder.io/qwik-city";
 
export const onGet: RequestHandler = async ({ cacheControl }) => {
    // 如果您希望浏览器使用 "stale-while-revalidate" 或 "s-maxage" 缓存控制头,您必须在 Vercel Edge 上添加第二个带有 "CDN-Cache-Control" 或 "Vercel-CDN-Cache-Control" 的 cacheControl
    cacheControl({
      staleWhileRevalidate: 60 * 60 * 24 * 365,
      maxAge: 5,
    });
    cacheControl({
      maxAge: 5,
      staleWhileRevalidate: 60 * 60 * 24 * 365,
    }, "CDN-Cache-Control");
};

Contributors

Thanks to all the contributors who have helped make this documentation better!

  • steve8708
  • harishkrishnan24
  • maiieul
  • hamatoyogi