Мы используем файлы cookie. Чтобы улучшить работу сайта и предоставить вам больше возможностей. Продолжая использовать сайт, высоглашаетесь с условиями использования cookie.

Разрабатываем PWA. Полная инструкция по работе с Web App Manifest и Service Worker

AGIMA16 апреля 2025Сергей Васильев·Фронтенд-разработчик

Для базового PWA нужны два компонента: Web App Manifest описывает установку и внешний вид приложения, а Service Worker управляет кешем, офлайн-режимом и сетевыми запросами. Важно также корректно настроить область действия и обновление воркера.

Коротко

КомпонентОтветственность
Web App ManifestНазвание, иконки, запуск и режим отображения
Service WorkerКеш, офлайн-режим и перехват запросов
ScopeСтраницы, которыми управляет воркер
LifecycleInstall, activate и рабочее состояние
UpdateДоставка новой версии без зависшего кеша

Привет! Меня зовут Сергей Васильев, я фронтенд-разработчик в AGIMA. Наша команда часто работает с PWA — прогрессивными веб-приложениями. Они стали особо популярны в последние три года, когда из-за санкций некоторым корпорациям пришлось отказаться от мобильных приложений. Но и раньше многие компании с интересом смотрели на это решение.

Ниже расскажу, как сделать из обычного веб-приложения прогрессивное: вместе настроим Web App Manifest и Service Worker. Если вам еще не доводилось работать с PWA — текст точно для вас.

Что такое PWA

PWA (Progressive Web App)

— это веб-приложение, способное предоставить человеку тот же пользовательский опыт, что и мобильное приложение. Вообще PWA работает в браузере, но его можно установить на домашний экран смартфона. Там его функции будут доступны в том числе офлайн.

Технология PWA стала широко известна в 2015 году благодаря расширению возможностей браузера Google Chrome и продвижению Service Worker и Web App Manifest.

Общий принцип такой:

чтобы превратить обычный сайт в PWA-приложение, нужно добавить файл Manifest и реализовать Service Worker. Причем второе необязательно, но сильно улучшит пользовательский опыт. Основной инструмент для отладки — DevTools, а именно вкладка Application.

Ресурсы, помогающие в создании и настройке PWA:

Web App Manifest

Manifest — это файл manifest.json, который содержит метаданные о вашем приложении. Этот файл предоставляет браузеру и операционной системе информацию о том, как отображать ваше веб-приложение и как оно должно взаимодействовать с пользователем. Manifest позволяет вашему веб-приложению выглядеть и вести себя как нативное приложение.

Первым делом создадим файл manifest.json и подключим его в секции Head нашего сайта:

text
// index.html
<link rel="manifest" crossorigin="use-credentials" href="/manifest.json">

Важно!

Файл manifest.json должен располагаться в корне проекта.

Пока Manifest пустой, но DevTools сразу подскажет, чего нам не хватает. Затем переходим на вкладку Application, ниже выбираем Manifest, смотрим перечень ошибок:

Ошибки пустого Web App Manifest в Chrome DevTools

И теперь заполняем. Обязательные поля:

  • name — полное название вашего приложения. Используется для отображения названия приложения в различных контекстах, таких как экран установки.
  • short_name — сокращенное название приложения, которое используется, когда полное название не может быть отображено полностью (например, на домашнем экране устройства).
  • start_url — URL, который будет открыт при запуске приложения. Определяет точку входа в приложение.
  • display — определяет предпочтительный режим отображения приложения. Например, standalone открывает приложение в полноэкранном режиме без элементов браузера. Влияет на то, как приложение будет отображаться пользователю.
  • icons — массив объектов, описывающих иконки вашего приложения. Каждая иконка должна иметь указание на путь к файлу (src), размеры (sizes) и тип (type). Хотя это поле не является строго обязательным, его отсутствие может привести к тому, что приложение не будет корректно отображаться на домашнем экране устройства.

Дополнительные поля:

  • background_color — цвет фона, который используется на этапе загрузки приложения.
  • theme_color — основной цвет темы, который может использоваться операционной системой для стилизации элементов интерфейса.
  • description — краткое описание вашего приложения.
  • orientation — определяет ориентацию экрана, в которой должно отображаться приложение.
  • display_override — если браузер умеет, то позволяет управлять элементами управления окна.
  • shortcuts — позволяют быстро получать доступ к определенным функциям или разделам приложения прямо с домашнего экрана устройства. Для шорткатов можно описать поля: name, short_name, description, url, icons.
  • screenshots — поле, в котором можно указать скриншоты, которые будут показаны при установке приложения.
  • capture_links и url_handlers — поля, позволяющие настроить автоматический переход в приложение при переходе по ссылке на сайт. На данный момент эти поля не относятся к стандартным в спецификации манифеста PWA. Поддержка таких функций может варьироваться в зависимости от браузера и его версии.

Пример заполненного манифеста:

text
// manifest.json
{
 "name": "Project",
 "short_name": "Project",
 "description": "Project - площадка для покупки и продажи",
 "start_url": "/?version=1",
 "version": "1",
 "lang": "ru-RU",
 "theme_color": "#21ac50",
 "background_color": "#21ac50",
 "display": "standalone",
 "display_override": ["window-controls-overlay"],
 "orientation": "portrait",
 "icons": [
 {
 "purpose": "any",
 "src": "/pwa/images/icons/icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },
 {
 "purpose": "any",
 "src": "/pwa/images/icons/icon-512.png",
 "sizes": "512x512",
 "type": "image/png"
 },
 {
 "purpose": "maskable",
 "src": "/pwa/images/icons/icon-512.png",
 "sizes": "512x512",
 "type": "image/png"
 }
 ],
 "shortcuts": [
 {
 "name": "Каталог объявлений",
 "url": "/rossiya/",
 "icons": [
 {
 "src": "/pwa/images/icons/icon-catalog.png",
 "sizes": "96x96",
 "type": "image/png"
 }
 ]
 },
 {
 "name": "Разместить",
 "url": "/personal/add/",
 "icons": [
 {
 "src": "/pwa/images/icons/icon-add.png",
 "sizes": "96x96",
 "type": "image/png"
 }
 ]
 },
 {
 "name": "Сообщения",
 "url": "/personal/messages/",
 "icons": [
 {
 "src": "/pwa/images/icons/icon-messages.png",
 "sizes": "96x96",
 "type": "image/png"
 }
 ]
 }
 ],
 "screenshots": [
 {
 "src": "/pwa/images/screenshots/desktop-1.png",
 "sizes": "2310x1866",
 "type": "image/jpg",
 "form_factor": "wide"
 },
 {
 "src": "/pwa/images/screenshots/desktop-2.png",
 "sizes": "2310x1866",
 "type": "image/jpg",
 "form_factor": "wide"
 },
 {
 "src": "/pwa/images/screenshots/mobile-1.jpg",
 "sizes": "720x1424",
 "type": "image/png",
 "form_factor": "narrow"
 },
 {
 "src": "/pwa/images/screenshots/mobile-2.jpg",
 "sizes": "720x1424",
 "type": "image/png",
 "form_factor": "narrow"
 }
 ],
 "capture_links": "existing_client_event",
 "url_handlers": [
 {
 "origin": "https://project.ru/"
 }
 ]
}

После этого на десктопе в адресной строке справа появится иконка, говорящая о том, что сайт является PWA и пользователь может установить его как приложение на компьютер. Вот как это выглядит:

Иконка установки PWA в адресной строке браузера

Service Workers

Сервис-воркеры — это скрипты, которые браузер выполняет в фоновом режиме, отдельно от веб-страницы. Основные функции сервис-воркеров:

  • Офлайн-работа. Сервис-воркеры могут кешировать ресурсы (например, HTML, CSS, JavaScript, изображения) при их первой загрузке, что позволяет приложению работать офлайн, предоставляя пользователям доступ к сохраненным данным.
  • Управление кешем. Вы можете программно управлять кешированием ресурсов, определяя, какие файлы и данные нужно хранить и как долго. Это позволяет улучшить производительность приложения за счет сокращения времени загрузки.
  • Фоновая синхронизация. Сервис-воркеры могут синхронизировать данные в фоновом режиме, когда устройство снова подключается к сети. Это полезно для обновления данных без необходимости вмешательства пользователя.
  • Push-уведомления. Сервис-воркеры могут обрабатывать push-уведомления, позволяя веб-приложениям отправлять сообщения пользователям, даже когда приложение не активно.

Область действия

Расположение файла сервис-воркера определяет область его действия. Сервис-воркер, расположенный по адресу example.com/my-pwa/sw.js, может управлять любой навигацией по пути my-pwa или ниже. Желательно создавать файл сервис-воркера в корне проекта.

Жизненный цикл

Жизненный цикл сервис-воркера начинается с регистрации. Затем браузер пытается загрузить и разобрать файл сервис-воркера. Если парсинг прошел успешно, запускается событие install. Это событие срабатывает только один раз.

Установка сервис-воркера происходит бесшумно, не требуя разрешения пользователя, даже если он не устанавливает PWA. API сервис-воркера доступен даже на платформах, не поддерживающих установку PWA, таких как Safari и Firefox на настольных устройствах.

После установки сервис-воркер необходимо активировать. Когда сервис-воркер будет готов управлять клиентами, сработает событие activate. Однако это не означает, что страница, зарегистрировавшая сервис-воркер, будет управляться. По умолчанию сервис-воркер возьмет управление на себя только при следующем переходе на эту страницу, либо в результате перезагрузки страницы, либо при повторном открытии PWA.

Прослушивать события в глобальной области видимости сервис-воркера можно с помощью объекта self:

text
// sw.js
self.addEventListener('install', (event) => {
 console.log('Service worker установлен');
});

self.addEventListener('activate', (event) => {
 console.log('Service worker активирован');
});

Обновление сервис-воркера

Сервис-воркеры обновляются, когда браузер обнаруживает, что установленная версия сервис-воркера отличается от того, что сейчас на сервере.

Важно!

Переименовывать сервис-воркер нельзя. Иначе браузер никогда не получит новую версию сервис-воркера.

После успешной установки новый сервис-воркер будет ожидать активации до тех пор, пока существующий (старый) сервис-воркер не перестанет контролировать клиентов. Это состояние называется ожиданием, и именно так браузер обеспечивает одновременную работу только одной версии сервис-воркера.

Обновление страницы или повторное открытие PWA не приведет к тому, что новый сервис-воркер возьмет управление на себя. Пользователю необходимо закрыть все вкладки и окна, использующие текущий сервис-воркер, или выйти из них, а затем вернуться обратно. Только после этого новый сервис-воркер возьмет управление на себя. Для получения более подробной информации можно почитать статью «Жизненный цикл сервис-воркера».

Пример сервис-воркера

В корне проекта создадим файл sw.js и добавим в него обработчики событий install, activate и fetch.

text
// sw.js
const CACHE_NAME = 'pwa-version-1';

const assets = [
 '/manifest.json',
 '/pwa/images/icons/icon-192.png',
 '/pwa/images/icons/icon-512.png',
 '/pwa/fonts/Montserrat-Medium.woff2',
 '/pwa/fonts/Montserrat-Medium.woff',
 '/fallback.html',
];

self.addEventListener('install', (evt) => {
 evt.waitUntil(self.skipWaiting().then(() => {
 return caches.open(CACHE_NAME)
 .then((cache) => {
 return cache.addAll(assets)
 .catch(err => {
 console.log('Failed to cache assets:', err)
 })
 })
 }));
});
  • CACHE_NAME — название кеша, в котором мы собираемся хранить данные. При изменении сервис-воркера необходимо изменить версию кеша. Это заставит сервис-воркер создать новый кеш и использовать обновленные ресурсы.
  • assets — массив, в котором указаны все ресурсы, которые необходимо закешировать. В нашем приложении мы кешируем только fallback-страницу, которая будет показана в том случае, если не загрузился сайт.

В первую очередь добавим обработчик на событие install. Это событие вызывается, когда сервис-воркер устанавливается в браузере. После установки создается кеш pwa-version-1 и все ресурсы из assets сохраняются в нем.

text
// sw.js
self.addEventListener('activate', (evt) => {
 evt.waitUntil(clients.claim().then(() => {
 return caches.keys().then(keys => {
 return Promise.all(keys.filter(key => key !== CACHE_NAME)
 .map(key => caches.delete(key)));
 })
 }));
});

Следующий код обрабатывает событие activate, которое вызывается, когда сервис-воркер становится активным и начинает управлять клиентами — например, вкладками браузера. Ищутся и удаляются все кеши, которые имеют название, отличное от CACHE_NAME. Это необходимо, чтобы освобождать место и избегать использования устаревших данных.

Далее напишем обработчик для события fetch. В данном случае он используется для того, чтобы отдавать ресурсы из кеша (если он там есть) и чтобы показывать заранее заготовленную fallback-страничку (когда страница недоступна).

text
// sw.js
self.addEventListener('fetch', event => {
 event.respondWith(caches.match(event.request)
 .then(response => {
 // Если ресурс найден в кэше, возвращаем его
 if (response) {
 return response;
 }

 // Если ресурс не найден в кэше, пытаемся получить его из сети
 return fetch(event.request).catch(() => {
 // Если запрос не удался (например, оффлайн), возвращаем резервную страницу
 return caches.match('./fallback.html');
 });
 }));
});

Файл сервис-воркера готов, осталось его зарегистрировать. В JS-код нашего сайта добавляем:

text
// script.js
if ('serviceWorker' in navigator) {
 // регистрация сервис-воркера
 navigator.serviceWorker.register('/service-worker.js')
 .then(reg => {
 reg.onupdatefo und = () => {
 const installingWorker = reg.installing;

 installingWorker.onstatecha nge = () => {
 if (installingWorker.state === 'installed' && navigator.serviceWorker.controller) {
 // Новая версия сервис-воркера доступна
 console.log('New service worker version available.');

 // Опционально: показать уведомление пользователю
 showUpdateNotification();
 }
 };
 };
 })
 .catch(err => console.log('service worker not registered', err));
 }

В код добавлен механизм отслеживания обновления сервис-воркера. Когда новый сервис-воркер будет установлен, он не начнет работать сразу, если в данный момент присутствует активный контроллер (то есть текущий сервис-воркер). Чтобы сервис-воркер обновился, нужно закрыть все вкладки сайта.

Итого

Теперь всё готово для запуска PWA. Мы добавили файл Manifest и реализовать Service Worker. В следующей статье покажу, с какими проблемами мы столкнулись на одном из проектов (в частности на iOS) и как их решали. Обсудить статью можно в нашем телеграм-канале для тимлидов.

PWAService WorkerWeb App Manifestfrontendофлайн-режим
Загружаем чат

Напишите в поддержку

Опишите проблему — команда разберётся.