Cache strategies

В рамках разработки веб-приложений критически важным аспектом производительности является эффективное управление кэшем. Фреймворк Fresh, ориентированный на Deno и использование современных стандартов веб-разработки, предоставляет гибкие инструменты для организации кэширования на разных уровнях приложения.


Основы кэширования

Кэширование в Fresh строится на принципах server-side rendering (SSR) и edge caching. Сервер может кэшировать как данные, так и полностью сгенерированные HTML-страницы. Основные преимущества кэширования:

  • Снижение нагрузки на сервер и базу данных.
  • Ускорение ответа клиенту.
  • Возможность реализации offline-first подходов при использовании service workers.

Кэш может быть инвалидационным (expire-based), статическим (immutable) или динамическим (на основе событий).


Кэширование страниц (Page Caching)

Fresh позволяет кэшировать страницы через HTTP-заголовки или встроенные механизмы edge-кэша. Основные параметры:

  • Cache-Control: управление временем жизни кэша.
  • ETag и Last-Modified: контроль изменения контента и условные запросы.
  • Vary: разделение кэша по типу запроса (например, язык, пользовательские куки).

Пример использования:

export const handler = async (req, ctx) => {
  const res = await ctx.render();
  res.headers.set("Cache-Control", "public, max-age=60, s-maxage=300");
  return res;
};

Объяснение: max-age задаёт время хранения кэша на клиенте, s-maxage — для edge и CDN. Такой подход позволяет оптимизировать отдачу страниц с минимальной задержкой.


Динамическое кэширование данных (Data Caching)

Для API-эндпоинтов и динамических данных Fresh предлагает хранить результаты запросов во встроенном кэше или внешних системах (Redis, Deno KV). Стратегии:

  1. Cache-then-network — сначала отдаётся кэшированное значение, параллельно обновляется актуальное.
  2. Network-first — сначала обращение к источнику, при сбое возвращается кэш.
  3. Stale-while-revalidate — отдаётся устаревший кэш с параллельной подгрузкой свежих данных.

Пример кэширования ответа API:

import { cache } from "https://deno.land/x/fresh_cache/mod.ts";

export const handler = async () => {
  const key = "users_list";
  let users = await cache.get(key);
  
  if (!users) {
    users = await fetch("https://api.example.com/users").then(res => res.json());
    await cache.set(key, users, { ttl: 300 }); // ttl в секундах
  }
  
  return new Response(JSON.stringify(users), {
    headers: { "Content-Type": "application/json" },
  });
};

Примечание: TTL (time to live) задаёт срок жизни кэша. После истечения значения обновляются при следующем запросе.


Edge caching и CDN интеграция

Fresh оптимально работает с edge-локациями, что позволяет реализовать глобальное кэширование. Для этого важно правильно конфигурировать:

  • CDN Headers (s-maxage, stale-while-revalidate).
  • Инвалидацию при изменении контента (purge или обновление версии).
res.headers.set(
  "Cache-Control",
  "public, max-age=60, s-maxage=600, stale-while-revalidate=30"
);

Особенности стратегии:

  • stale-while-revalidate позволяет отдавать устаревший кэш, пока параллельно обновляется свежий.
  • s-maxage управляет TTL для CDN и edge-кэшей, что снижает нагрузку на исходный сервер.

Кэширование компонентов и ресурсов

В Fresh можно кэшировать отдельные компоненты React/Preact, что уменьшает время рендеринга сложных страниц. Используются memoization и встроенные функции cache:

  • cachePageComponent — кэширование результата рендера компонента.
  • cacheData — кэширование вычисленных данных для компонента.

Пример:

import { cache } from "https://deno.land/x/fresh_cache/mod.ts";

const UserList = cache(async () => {
  const users = await fetch("https://api.example.com/users").then(res => res.json());
  return (
    <ul>
      {users.map(user => <li>{user.name}</li>)}
    </ul>
  );
}, { ttl: 300 });

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


Best practices

  • Разделять статический и динамический контент, чтобы минимизировать частоту инвалидации кэша.
  • Использовать stale-while-revalidate для страниц с высокой посещаемостью.
  • Минимизировать TTL для данных, которые часто обновляются, и увеличивать для редко меняющихся.
  • Кэшировать не только HTML, но и JSON-данные, изображения и статические ресурсы.
  • Контролировать кэш с помощью заголовков Vary, чтобы корректно обслуживать разные версии страницы (например, мобильная и десктопная).

Эти стратегии позволяют строить быстрые, масштабируемые приложения на Fresh, используя гибкое управление кэшированием как на стороне сервера, так и на edge. Эффективное кэширование сокращает задержки, снижает нагрузку на API и обеспечивает высокую отзывчивость интерфейса.