Как стать автором
Обновить
128.45
AGIMA
Крупнейший интегратор digital-решений

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

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров4.5K

Привет! Меня зовут Сергей Васильев, я фронтенд-разработчик в 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 нашего сайта:

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

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

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

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

  • 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. Поддержка таких функций может варьироваться в зависимости от браузера и его версии.

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

// 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 и пользователь может установить его как приложение на компьютер. Вот как это выглядит:

Service Workers

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

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

  • Управление кешем. Вы можете программно управлять кешированием ресурсов, определяя, какие файлы и данные нужно хранить и как долго. Это позволяет улучшить производительность приложения за счет сокращения времени загрузки.

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

  • Push-уведомления. Сервис-воркеры могут обрабатывать push-уведомления, позволяя веб-приложениям отправлять сообщения пользователям, даже когда приложение не активно.

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

// 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 сохраняются в нем.

// 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-страничку (когда страница недоступна).

// 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-код нашего сайта добавляем:

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

          installingWorker.onstatechange = () => {
            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) и как их решали. Также обсудить статью можно в нашем телеграм-канале для тимлидов.

Что еще почитать

Теги:
Хабы:
+19
Комментарии18

Публикации

Информация

Сайт
www.agima.ru
Дата регистрации
Дата основания
Численность
501–1 000 человек
Местоположение
Россия
Представитель
Кристина Ляпцева