Нажимая кнопку «Отправить», вы принимаете правилами обработки персональных данных
Заявка отправлена

Спасибо за проявленный интерес к нашей компании, специалист свяжется с вами в ближайшее время

Услуги
Веб-разработка
Разработка мобильных приложений
Автоматизация бизнеса
UX/UI дизайн
Техподдержка интернет-проектов 24/7 по SLA
Digital-продвижение
Обработка данных
Наша работа
Кейсы
Нажимая кнопку «Отправить», вы принимаете правилами обработки персональных данных
Заявка отправлена

Спасибо за проявленный интерес к нашей компании, специалист свяжется с вами в ближайшее время

Кеширование Lottie-анимаций с помощью Service Worker

< Все публикации
октябрь 2025 ~ 7 минИсточник Хабр
Леонид Прокошкин
1.jpg

Всем привет! Меня зовут Прокошкин Леонид, я Frontend-разработчик в компании DDPlanet.

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

В проекте было около 30 Lottie-анимаций, некоторые весили ~100 KB, и нам нужно было оптимизировать их загрузку.

Как снизить трафик и нагрузку? Правильно — кеширование. Мы выбрали кеширование на стороне Service Worker.

Сравнение скорости загрузки

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

Загрузка из сети (~1300 ms):

2.jpeg

Загрузка из кеша (~1.5 ms):

3.jpeg

Думаю, разница очевидна, итак, перейдем к реализации

Почему именно Service Worker?

Service Worker — это фоновый скрипт, который работает отдельно от основного потока браузера. Его ключевые возможности:

  • Перехват сетевых запросов: можно решать, отдавать ли ответ из сети, из кеша или возвращать кастомный ответ.
  • Гибкое кеширование: сохранять файлы в Cache Storage, управлять временем жизни и условиями обновления.
  • Работа в оффлайне: даже если интернет недоступен, можно возвращать данные из кеша.
  • Фоновая обработка: например, проверка обновлений, синхронизация или очистка устаревших данных.

Теперь разберемся, почему не подходят другие популярные варианты:

Почему не localStorage

API синхронный — любое чтение/запись блокирует основной поток JS. Для JSON-анимаций по 100 KB это вызывает лаги. Очень маленький лимит хранилища (обычно 5–10 MB). Нет встроенного механизма версионирования — ключи и очистку придется писать вручную.

Почему не IndexedDB

API асинхронный и довольно сложный: транзакции, версии схем, обработка ошибок. Отлично подходит для структурированных данных, но не ведет себя как HTTP-кеш для файлов. Чтобы связать IndexedDB с fetch, нужны обертки и собственные механизмы TTL и версионирования.

Почему именно Service Worker + Cache Storage

Запрос остается обычным fetch, а SW решает — вернуть ответ из кеша или сети. (не нужно переписывать готовые запросы, чтобы внедрить SW) Хранение оптимизировано браузером под бинарные файлы и JSON. Time To Live и версионирование удобно реализовать через query-параметры (?ts=...). Не блокирует основной поток: все выполняется в фоне.

Реализация

1) Service Worker

Создадим файл service-worker.js.

4.jpeg

Теперь нам необходимо обработать события SW

install: установка воркера

Событие install запускается, когда браузер скачал новую версию SW и пытается ее установить.

— self.skipWaiting() пропускает стадию waiting — новая версия сразу перейдет к событию activate.

— event.waitUntil(caches.open(CACHE_NAME)) мы говорим что событие install будет ждать открытия кеша и перейдет к activate после успешного открытия, нам это необходимо для того чтобы к моменту активации мы точно знали что кеш есть, а так же быстро активируем обновления SW, если они есть.

Важно учитывать! skipWaiting() опасен при несовместимости активов: новая логика может обслуживать старые вкладки. Предкеш критичных файлов лучше тоже делать внутри waitUntil.
5.jpeg

activate: захват клиентов

— Событие activate приходит сразу после успешного события install.

— clients.claim() — новый SW берет контроль над всеми открытыми вкладками (без их перезагрузки).

— «Миграция» кешей: удаляем все кеши, имена которых не равны CACHE_NAME. (это актуально если мы используем версионность кешей, в данном примере у нас этого нет)

— Настраиваем ttlMs (Time to Live ms) через Storage API: чем больше квота, тем дольше храним.

Важно учитывать! clients.claim() + skipWaiting() = быстрые апдейты, но думайте о совместимости со старой страницей. navigator.storage.estimate() может вернуть undefined — нужно писать дефолтный estimate, но в рамках разумного.
6.jpeg

cleanupCacheIfNeeded: «ленивая» (пороговая) очистка по ttlMs

Проверяем, прошло ли ttlMs с момента последней чистки (lastCleanup). Если нет — выходим быстро. Если пора чистить: обходим все ключи кеша и удаляем записи, у которых метка ts старше ttlMs.

Почему ?ts= в ключе:

— Ключ кэша — это URL. Добавляя ?ts=..., фиксируем момент записи.

— При чтении используем ignoreSearch: true, но для очистки извлекаем ts из ключа.

7.jpeg

fetch: перехват запросов

Обрабатываем только GET-запросы к файлам стикеров и пакету.

Cache First: сначала ищем в кеше (ignoreSearch: true, чтобы игнорировать наши query параметры ?ts..), если нет — идем в сеть, сохраняем копию с ?ts.

— При оффлайне возвращаем кеш (если есть), иначе — минимальный ответ 504.

Важно учитывать! ignoreSearch: true важно, потому что ключи содержат ?ts= Для UX вместо 504 можно, например, вернуть плейсхолдер анимацию или обработать по Вашему усмотрению
8.jpeg

2) Хук для использования Service Worker

Сам по себе Service Worker живет отдельно и ничего не знает о бизнес логике приложения. Чтобы связать его с клиентской частью, нужно:

  1. Зарегистрировать воркер через navigator.serviceWorker.register.
  2. Дождаться готовности (navigator.serviceWorker.ready).
  3. Организовать удобный слой взаимодействия, я сделал это через хук.

Перейдем к реализации

Константы окружения и маршруты

9.jpeg

Регистрация SW и базовая подготовка кеша

Регистрируем SW, ждем, когда сработает событие ready и открываем кеш.

10.jpeg

Чтение пакета из кеша

Пытаемся найти пакет по ключу GET_STICKER_ROUTE (ignoreSearch: true, не забываем). Если нашли — парсим JSON. Поврежденные записи безопасно игнорируем.

11.jpeg

Первый запуск: загрузка пакета

Если пакета нет в кеше — тянем из сети (SW положит ответ в кеш), затем заранее скачиваем файлы .lottie и сохраняем сам пакет с ?ts= для участия в TTL очистке.

12.jpeg

Проверка и применение обновлений

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

13.jpeg

Итог

В итоге мы свели загрузку Lottie-анимаций к одному обращению в сеть и дальнейшей выдаче из Cache Storage. Это ощутимо ускорило повторную загрузку стикеров, мы избавились от жесткого расхода трафика, убрали лишнюю нагрузку на сервер, стикеры теперь крутятся даже оффлайн, пользователи рады.

Связаться с нами
Форматы: jpg, png, xls, xlsx, doc, docx, pdf
Размер до 5 МБ
Нажимая кнопку «Отправить», вы принимаете правилами обработки персональных данных
Заявка отправлена

Спасибо за проявленный интерес к нашей компании, специалист свяжется с вами в ближайшее время