22. Кэширование HTTP
Remix позволяет управлять HTTP кэшированием через заголовки Cache-Control прямо из loader функций. Это даёт полный контроль над тем, что кэшируется и на сколько.
Cache-Control в loader
Заголовок раздела «Cache-Control в loader»export async function loader({ params }) { const post = await getPost(params.slug);
return json(post, { headers: { // Публичный кэш на 1 час, stale-while-revalidate на 1 день "Cache-Control": "public, max-age=3600, stale-while-revalidate=86400", }, });}Стратегии кэширования
Заголовок раздела «Стратегии кэширования»no-store (нет кэша)
Заголовок раздела «no-store (нет кэша)»"Cache-Control": "no-store"// Использовать для: персональные данные, финансовые данныеno-cache (всегда проверять)
Заголовок раздела «no-cache (всегда проверять)»"Cache-Control": "no-cache"// Ресурс кэшируется, но всегда проверяется на свежестьmax-age (статичные данные)
Заголовок раздела «max-age (статичные данные)»"Cache-Control": "public, max-age=31536000, immutable"// Использовать для: статические ресурсы с хэшами в URLstale-while-revalidate
Заголовок раздела «stale-while-revalidate»"Cache-Control": "public, max-age=60, stale-while-revalidate=3600"// Показываем кэш на 60с, обновляем в фоне до 1 часаETag для условных запросов
Заголовок раздела «ETag для условных запросов»export async function loader({ request, params }) { const post = await getPost(params.slug); const etag = \`"\${post.updatedAt.getTime()}"\`;
// Если ETag совпадает — возвращаем 304 if (request.headers.get("If-None-Match") === etag) { return new Response(null, { status: 304 }); }
return json(post, { headers: { "Cache-Control": "public, max-age=60", "ETag": etag, }, });}Варьирование кэша (Vary)
Заголовок раздела «Варьирование кэша (Vary)»export async function loader({ request }) { const acceptLanguage = request.headers.get("Accept-Language"); const lang = parseLanguage(acceptLanguage); const content = await getLocalizedContent(lang);
return json(content, { headers: { "Cache-Control": "public, max-age=3600", "Vary": "Accept-Language", // разный кэш для разных языков }, });}HTTP кэш vs клиентский кэш
Заголовок раздела «HTTP кэш vs клиентский кэш»HTTP Cache (правильный подход в Remix): ✅ Работает без JS ✅ Общий для всех пользователей (CDN) ✅ Стандартные заголовки ✅ Автоматически инвалидируется при навигации
Клиентский кэш (React Query, SWR): ⚠️ Только при наличии JS ⚠️ Только для текущего пользователя ⚠️ Нужна ручная инвалидация ✅ Мгновенные UI обновленияИнвалидация кэша
Заголовок раздела «Инвалидация кэша»Remix автоматически инвалидирует кэш при:
- Отправке формы (action вызывается)
- Программной навигации с revalidate
- Ручном вызове
revalidate()
import { useRevalidator } from "@remix-run/react";
function AutoRefresh() { const revalidator = useRevalidator();
useEffect(() => { const id = setInterval(() => { if (revalidator.state === "idle") { revalidator.revalidate(); } }, 30_000); // обновляем каждые 30 секунд
return () => clearInterval(id); }, [revalidator]);}