缓存响应

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

一个好的默认做法是为所有响应使用 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({
    // Always serve a cached response by default, up to a week stale
    staleWhileRevalidate: 60 * 60 * 24 * 7,
    // Max once every 5 seconds, revalidate on the server to get a fresh version of this page
    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";
 
// Override caching for /dashboard pages to not cache as they are unique per visitor
export const onGet: RequestHandler = async ({ cacheControl }) => {
  cacheControl({
    public: false,
    maxAge: 0,
    sMaxAge: 0,
    staleWhileRevalidate: 0,
  });
};
 

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

何时不缓存

缓存通常是有益的,但并非始终适用于所有页面。如果您的网站包含对不同用户显示不同内容的 URL(例如,仅供已登录用户访问的页面或根据用户位置显示内容的页面),则应避免使用缓存控制头来缓存这些页面。相反,应在服务器端为每个访问者渲染这些页面的内容。

对于对所有人看起来都一样的流量很大的页面(例如主页),缓存非常适合提高性能和降低成本。对于专门针对已登录用户且流量可能较少的页面,建议禁用缓存。

您可以使用任何您喜欢的逻辑有条件地更改缓存行为。

src/routes/layout.tsx
import type { RequestHandler } from "@builder.io/qwik-city";
 
export const onGet: RequestHandler = async ({ cacheControl, url }) => {
  // Only our homepage is public and should be CDN cached. Other pages are unique per visitor
  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 的文档中

如果您设置了 Cache-Control 但没有 CDN-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 }) => {
    // If you want the browser to use "stale-while-revalidate" or "s-maxage" Cache Control headers, you have to add the second cacheControl with "CDN-Cache-Control" or "Vercel-CDN-Cache-Control" on Vercel Edge 
    cacheControl({
      staleWhileRevalidate: 60 * 60 * 24 * 365,
      maxAge: 5,
    });
    cacheControl({
      maxAge: 5,
      staleWhileRevalidate: 60 * 60 * 24 * 365,
    }, "CDN-Cache-Control");
};

贡献者

感谢所有帮助改进此文档的贡献者!

  • steve8708
  • harishkrishnan24
  • maiieul
  • igorbabko
  • mrhoodz
  • mhevery
  • chsanch
  • hamatoyogi
  • Jemsco