Кеширование — один из наиболее эффективных способов ускорить загрузку сайта. Правильно настроенный кеш позволяет отдавать ресурсы мгновенно, без обращения к серверу. Понимание механизмов кеширования критично для любого веб-разработчика.
Сервер управляет кешированием через заголовок Cache-Control. Основные директивы:
`max-age=seconds` — ресурс актуален в течение указанного времени. Например, max-age=31536000 кеширует на год. Браузер не делает запрос к серверу, пока время не истекло.
`no-cache` — кешировать нельзя без проверки. При каждом запросе браузер отправляет условный запрос к серверу. Если ресурс не изменился, сервер отвечает 304 Not Modified без тела — экономия трафика.
`no-store` — не кешировать вообще. Каждый раз полный запрос и полный ответ. Используется для чувствительных данных.
`public` — ресурс можно кешировать на CDN-узлах и прокси-серверах.
`private` — только в кеше конкретного пользователя, не на CDN.
`immutable` — ресурс никогда не изменится. Браузер не будет проверять его актуальность. Используется с хешированными именами файлов.
При условном запросе браузер отправляет заголовки:
If-None-Match: "abc123" — если ETag не изменился, сервер отвечает 304If-Modified-Since: Thu, 01 Jan 2025 00:00:00 GMT — если файл не менялся, сервер отвечает 304Ответ 304 не содержит тела — это экономит трафик при большом количестве ресурсов.
Если файл меняется при каждом деплое, нужно принудительно сбросить кеш у пользователей. Стандартное решение — хешированные имена файлов: app.a3f2b1c.js вместо app.js. При каждом изменении файла его хеш меняется, и браузер загружает новую версию. При этом неизменившиеся файлы сохраняют старые хеши и остаются в кеше.
Service Worker — это скрипт, который работает в фоне браузера, перехватывает сетевые запросы и отвечает из кеша. Cache API позволяет программно управлять кешем:
caches.open('v1') // открыть (или создать) именованный кеш
cache.put(url, response) // сохранить ответ
cache.match(url) // найти в кеше
cache.delete(url) // удалить запись
caches.delete('v1') // удалить весь кешСтратегии кеширования в Service Worker:
PerformanceResourceTiming позволяет определить, откуда пришёл ресурс:
transferSize === 0 — ресурс из кеша браузера (disk cache или memory cache)transferSize > 0 && encodedBodySize > 0 — загружен с сервераtransferSize > 0 && encodedBodySize === 0 — сервер ответил 304 (revalidation)Cache API, проверка источника ресурсов через Resource Timing
// Cache API — программное кеширование (обычно используется в Service Worker)
// Открываем именованный кеш
const cache = await caches.open('api-cache-v1')
// Кешируем запрос вручную
async function fetchWithCache(url) {
// Ищем в кеше
const cached = await cache.match(url)
if (cached) {
console.log('Из кеша:', url)
return cached.json()
}
// Нет в кеше — загружаем с сервера
const response = await fetch(url)
// Клонируем ответ: тело можно прочитать только раз
cache.put(url, response.clone())
console.log('С сервера:', url)
return response.json()
}
const data = await fetchWithCache('https://jsonplaceholder.typicode.com/posts/1')
console.log('Данные:', data.title)
// Повторный вызов — уже из кеша
const dataCached = await fetchWithCache('https://jsonplaceholder.typicode.com/posts/1')
// Проверяем источник ресурсов через Resource Timing
window.addEventListener('load', () => {
const resources = performance.getEntriesByType('resource')
resources.forEach(entry => {
let source
if (entry.transferSize === 0) {
source = 'кеш браузера' // disk/memory cache
} else if (entry.encodedBodySize === 0) {
source = 'сервер (304 Not Modified)'
} else {
source = `сервер (${entry.transferSize} байт)`
}
console.log(`${entry.name.split('/').pop()}: ${source}`)
})
})Кеширование — один из наиболее эффективных способов ускорить загрузку сайта. Правильно настроенный кеш позволяет отдавать ресурсы мгновенно, без обращения к серверу. Понимание механизмов кеширования критично для любого веб-разработчика.
Сервер управляет кешированием через заголовок Cache-Control. Основные директивы:
`max-age=seconds` — ресурс актуален в течение указанного времени. Например, max-age=31536000 кеширует на год. Браузер не делает запрос к серверу, пока время не истекло.
`no-cache` — кешировать нельзя без проверки. При каждом запросе браузер отправляет условный запрос к серверу. Если ресурс не изменился, сервер отвечает 304 Not Modified без тела — экономия трафика.
`no-store` — не кешировать вообще. Каждый раз полный запрос и полный ответ. Используется для чувствительных данных.
`public` — ресурс можно кешировать на CDN-узлах и прокси-серверах.
`private` — только в кеше конкретного пользователя, не на CDN.
`immutable` — ресурс никогда не изменится. Браузер не будет проверять его актуальность. Используется с хешированными именами файлов.
При условном запросе браузер отправляет заголовки:
If-None-Match: "abc123" — если ETag не изменился, сервер отвечает 304If-Modified-Since: Thu, 01 Jan 2025 00:00:00 GMT — если файл не менялся, сервер отвечает 304Ответ 304 не содержит тела — это экономит трафик при большом количестве ресурсов.
Если файл меняется при каждом деплое, нужно принудительно сбросить кеш у пользователей. Стандартное решение — хешированные имена файлов: app.a3f2b1c.js вместо app.js. При каждом изменении файла его хеш меняется, и браузер загружает новую версию. При этом неизменившиеся файлы сохраняют старые хеши и остаются в кеше.
Service Worker — это скрипт, который работает в фоне браузера, перехватывает сетевые запросы и отвечает из кеша. Cache API позволяет программно управлять кешем:
caches.open('v1') // открыть (или создать) именованный кеш
cache.put(url, response) // сохранить ответ
cache.match(url) // найти в кеше
cache.delete(url) // удалить запись
caches.delete('v1') // удалить весь кешСтратегии кеширования в Service Worker:
PerformanceResourceTiming позволяет определить, откуда пришёл ресурс:
transferSize === 0 — ресурс из кеша браузера (disk cache или memory cache)transferSize > 0 && encodedBodySize > 0 — загружен с сервераtransferSize > 0 && encodedBodySize === 0 — сервер ответил 304 (revalidation)Cache API, проверка источника ресурсов через Resource Timing
// Cache API — программное кеширование (обычно используется в Service Worker)
// Открываем именованный кеш
const cache = await caches.open('api-cache-v1')
// Кешируем запрос вручную
async function fetchWithCache(url) {
// Ищем в кеше
const cached = await cache.match(url)
if (cached) {
console.log('Из кеша:', url)
return cached.json()
}
// Нет в кеше — загружаем с сервера
const response = await fetch(url)
// Клонируем ответ: тело можно прочитать только раз
cache.put(url, response.clone())
console.log('С сервера:', url)
return response.json()
}
const data = await fetchWithCache('https://jsonplaceholder.typicode.com/posts/1')
console.log('Данные:', data.title)
// Повторный вызов — уже из кеша
const dataCached = await fetchWithCache('https://jsonplaceholder.typicode.com/posts/1')
// Проверяем источник ресурсов через Resource Timing
window.addEventListener('load', () => {
const resources = performance.getEntriesByType('resource')
resources.forEach(entry => {
let source
if (entry.transferSize === 0) {
source = 'кеш браузера' // disk/memory cache
} else if (entry.encodedBodySize === 0) {
source = 'сервер (304 Not Modified)'
} else {
source = `сервер (${entry.transferSize} байт)`
}
console.log(`${entry.name.split('/').pop()}: ${source}`)
})
})Реализуй класс RequestCache, который кеширует fetch-запросы в Map. Методы: get(url) — возвращает данные из кеша или загружает и кеширует. clear() — очищает весь кеш. has(url) — проверяет, есть ли URL в кеше. Повторный вызов get() для одного URL не должен делать новый запрос.
Используй Map — он имеет методы has(), get(), set(), clear(). В методе get() сначала проверь this.cache.has(url), если есть — верни this.cache.get(url). Если нет — загрузи, сохрани через this.cache.set(url, data) и верни data.