Кэширование ответов

Кэширование ответов имеет решающее значение для обеспечения максимально возможной скорости работы вашего сайта, как для страниц, так и для конечных точек.

Хорошим вариантом по умолчанию является использование stale-while-revalidate-кэширования для всех ответов.

Например, мы можем добавить экспорт onGet в наш корневой макет (src/routes/layout.tsx) следующим образом, чтобы применить хорошие настройки кэширования по умолчанию для всего сайта:

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, может вызвать 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-адреса, которые будут показывать разное содержимое разным людям - например, страницы только для пользователей, вошедших в систему, или страницы, которые показывают разное содержимое в зависимости от местоположения пользователя, вам не следует кэшировать эти страницы с помощью заголовков cache-control в ответе, а следует рендерить содержимое этих страниц на стороне сервера для каждого посетителя.

Для страниц с высокой посещаемостью, которые выглядят одинаково для всех, таких как домашняя страница, кэширование является лучшим решением с точки зрения производительности и стоимости. Но для страниц, предназначенных только для залогиненных пользователей и, как следствие, вероятно, имеющих несколько меньший трафик, отключение кэширования может быть разумным.

Вы можете условно изменить поведение кэша с помощью любой логики, наподобие этой:

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

Если задать 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 }) => {
  // Если вы хотите, чтобы браузер использовал заголовки Cache Control "stale-while-revalidate" или "s-maxage", необходимо добавить второй cacheControl с параметром "CDN-Cache-Control" или "Vercel-CDN-Cache-Control".
  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