Предыдущие стратегии - Cache First, Network First, Stale While Revalidate - по-разному разменивают свежесть на доступность, но все перехватывают запрос и по возможности отвечают из кэша. Естественно выбрать одну любимую стратегию и применить её ко всему приложению. Но в любом приложении есть ресурсы, где правильный ход противоположный: сознательно никогда не кэшировать или сознательно никогда не ходить в сеть. Это две крайние стратегии - Network Only и Cache Only.
Network Only означает, что запрос идёт напрямую в сеть, service worker не подменяет его кэшем, а сетевая ошибка честно всплывает как ошибка. Так стоит обращаться с мутациями, чувствительными endpoint'ами, realtime-проверками и телеметрией. Устаревший кэшированный ответ здесь - это ложь: если на вопрос 'свободен ли ещё этот слот' или на списание платежа ответить старыми данными, вы повредите домен, а не ускорите его.
Возьмём выездное приложение заметок, которое инспектор ведёт на нестабильной связи. Отправка новой заметки - это POST, и отвечать на POST из кэша нельзя. Нет сети - ошибку обрабатывает продукт: ставит команду в очередь, а не подсовывает старый ответ, делая вид, что всё прошло. Background Sync - очередь повторной доставки команды, а не стратегия кэширования. Повтор обязан быть идемпотентным и нести тот же operation ID, иначе дважды доставленная команда создаст две заметки вместо одной.
Cache Only - зеркальная крайность: ответ берётся только из Cache Storage, в сеть worker не ходит никогда. Это оправдано для гарантированно precached-ресурса или заранее скачанного offline-пакета - справочников и карт участка, которые инспектор загрузил перед выездом. Ключевое условие: cache miss должен быть невозможен по контракту либо иметь fallback. Отдадите Cache Only то, что не положили в кэш заранее, - офлайн получите жёсткий отказ без запасного пути.
Цена Cache Only - в том, что инвалидацию вы берёте на себя целиком. Ничто само не обновит этот пакет: он живёт ровно в том виде, в каком его положили. Это дёшево и мгновенно, но устаревший офлайн-пакет - ваша ответственность, и его версию ведёте руками наравне с версией приложения.
Одно приложение почти всегда сочетает несколько стратегий, и распределение между ними - не техническая мелочь, а часть продуктового контракта. Прежде чем переносить всё в Workbox routes, стоит выписать для каждого маршрута: владельца, бюджет свежести (freshness budget), ценность в офлайне, предельный размер в байтах, способ инвалидации, чувствительность к авторизации и fallback.
| Класс | Стратегия | Ограничитель |
|---|---|---|
| Hashed JS/CSS/fonts | Precache / Cache First | Revision + cleanup |
| HTML navigation | Network First | Timeout + offline fallback |
| Images | Cache First или SWR | maxEntries + maxAge |
| Public GET API | Network First / SWR | schema version + TTL |
| Private GET API | IndexedDB/domain cache или network | user namespace + logout purge |
| Mutation | Network Only + outbox | idempotency + conflict policy |
Хэшированные JS, CSS и шрифты content hash опознаёт по содержимому сам, поэтому их кладут в precache с очисткой старых ревизий. HTML-навигацию ведёт Network First с таймаутом и офлайн-fallback. Картинки - Cache First или SWR с лимитами maxEntries и maxAge. Публичный GET-API - Network First или SWR с версией схемы и TTL. Приватный GET - доменный кэш в IndexedDB под namespace пользователя с очисткой при logout. Мутации - Network Only плюс outbox с идемпотентностью и политикой конфликтов.
Показательный провал - применить одну стратегию ко всему сразу. Cache First на HTML-навигации заморозит людей на устаревшем билде; Network First на хэшированных ассетах жжёт сеть на неизменных по определению файлах; кэш приватного GET без очистки при logout на общем устройстве покажет следующему пользователю чужие данные. Матрица нужна затем, чтобы каждый из этих отказов не случился по недосмотру.