Перейти к содержанию

Сделайте PWA автономным

Добро пожаловать на День 5 из #30DaysOfPWA! Хотите узнать больше об этом проекте? Ознакомьтесь с нашим постом Вступление, чтобы получить более подробную информацию о дорожной карте контента и участниках. А теперь давайте погрузимся в работу!

День 5: Поговорим о сервис-воркерах!

Что вы узнаете сегодня

  • Что означает "офлайн"? Каким должен быть опыт PWA?
  • Как PWA узнает об изменениях в сети?
  • Какие варианты хранения данных могут использовать сервис-воркеры?
  • Какие стратегии кэширования могут использовать PWA?

Давайте подведем итоги

Что мы узнали к настоящему моменту:.

  • PWA — это веб-приложения, использующие прогрессивное улучшение для увеличения или уменьшения масштаба возможностей в соответствии с возможностями устройств и браузеров. На более мощных устройствах они могут быть неотличимы от установленных приложений для конкретной платформы.
  • PWA используют открытые веб-технологии, основными элементами которых являются HTTPS (безопасность), сервис-воркеры (надежность) и Web App Manifest (возможность установки).
  • Сервис-воркеры — это тип веб-рабочего, который действует как сетевой прокси (перехватывает запросы на получение), интеллектуально управляет кэш-памятью (для работы в автономном режиме) и обрабатывает асинхронные события, такие как push-уведомления (для поддержки повторно подключаемого поведения). Сегодня в центре внимания:

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

Что означает термин "автономный"?

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

Сервис-воркеры могут помочь сделать PWA независимыми от сети, обеспечив их работоспособность даже в условиях нестабильной или отсутствующей сети. К числу рекомендуемых практик относятся:

  • Предоставление пользовательской автономной страницы. Вместо того чтобы показывать стандартную автономную страницу браузера, можно показать что-то значимое, что позволит пользователю оставаться в приложении до тех пор, пока не восстановится сетевое соединение. Например, можно предварительно кэшировать пользовательскую автономную страницу, отражающую брендинг приложения; показывать ее, когда пользователь находится в автономном режиме и переходит на некэшированную страницу или маршрут, что дает ему уверенность в том, что приложение отвечает за работу и знает о текущем состоянии сети.
  • Проактивное кэширование ресурсов или ответов с длительным сроком хранения. В вашем приложении может быть контент, который остается неизменным в течение длительных промежутков времени — например, изображения баннеров, состояние аутентификации, медиафайлы для воспроизведения и т.д. Проактивно кэшируйте и используйте их даже в режиме онлайн для повышения производительности и улучшения поведения, специфичного для конкретной платформы.

Теперь, когда сервис-воркеры перехватывают запрос "fetch", они могут оценить различные стратегии возврата ответа. Например: стратегия "сначала офлайн" может агрессивно отдавать приоритет кэшу, а не сети при возврате ответов. Хотя это может дать преимущества в производительности (уменьшение сетевых задержек), это также требует управления приоритетами кэшированных элементов для воздействия на них.

В следующей паре разделов мы рассмотрим код, который поможет нам реализовать эти стратегии в нашем сервисе-воркере!

Но сначала потратьте минуту на изучение выбранного вами Sample PWA и посмотрите на реализацию его сервис-воркера. Сохраните ее открытой во вкладке, чтобы иметь возможность изучить ее в контексте данного обсуждения. Я использую DevTools Tips и сделал копию его файла sw.js в this gist для справки.

Понимание возможностей хранения данных

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

  • CacheStorage — API к хранилищу именованных объектов Cache, к которым могут обращаться как сервис-воркеры, так и основной поток JavaScript приложения. В кэшах хранятся пары запрос/ответ для сетевых ресурсов. Кэши должны управляться явным образом — для обновления и удаления — с установкой квот для каждого источника. При необходимости сервис-воркер может иметь несколько именованных объектов Cache.
  • IndexedDB — API для хранения больших объемов структурированных данных, включая файлы и блобы. Это объектно-ориентированная транзакционная база данных, использующая пары ключ-значение и идеально подходящая для хранения отдельных ресурсов (по сравнению со страницами ответов в кэше). Более подробно об IndexedDB можно прочитать на неделе 4 "Платформы и практики".

Обратите внимание, что опции Web Storage (localStorage и sessionStorage) являются синхронными и не могут использоваться внутри веб-рабочих, но их можно использовать из главного потока с потенциальным снижением производительности. Подробнее об этом здесь. Хотите получить более полное представление о возможностях хранения данных? Просто просмотрите панель приложений PWA с помощью DevTools и выполните отладку в интерактивном режиме.

изображение осмотра панели приложений

Ознакомьтесь с документацией Microsoft Edge по панелям Сервис-воркеры, Хранилище кэша и IndexedDB, чтобы узнать, как исследовать их в реальных условиях.

Хранение кэша и стратегии

Кэши должны управляться явным образом — создание, модификация и удаление ресурсов должно осуществляться сервис-воркером намеренно. Использование кэша зависит от стратегии, используемой для обработки запросов на выборку. Offline Cookbook — это отличный ресурс, который дает представление обо всех трех вопросах:

  • что кэшировать (ресурсы реального времени против долгоживущих типов ресурсов)
  • когда кэшировать (при установке, активации, событиях выборки)
  • как обрабатывать запросы на выборку (сначала кэш, сначала сеть, комбинация).

Давайте рассмотрим несколько стратегий, используя код из примера PWA.

1. Кэширование при установке для повышения производительности

Вот соответствующий фрагмент кода из гиста DevTools Tips sw.js, аннотированный моими комментариями для ясности

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// named cache in Cache Storage
const CACHE_NAME = 'devtools-tips-v3';

// list of requests whose responses will be pre-cached at install
const INITIAL_CACHED_RESOURCES = [
    '/',
    '/offline/',
    '/all/',
    '/browser/edge/',
    '/browser/safari/',
    '/browser/firefox/',
    '/browser/chrome/',
    '/assets/style.css',
    '/assets/filter-tip-list.js',
    '/assets/share.js',
    '/assets/logo.png',
    'https://unpkg.com/[email protected]/themes/prism-okaidia.css',
    '/assets/localforage-1.10.0.min.js',
];

// install event handler (note async operation)
// opens named cache, pre-caches identified resources above
self.addEventListener('install', (event) => {
    event.waitUntil(
        (async () => {
            const cache = await caches.open(CACHE_NAME);
            cache.addAll(INITIAL_CACHED_RESOURCES);
        })(),
    );
});

При этом ключевые пары запрос-ответ предварительно кэшируются для готовности к работе в автономном режиме. Но как они извлекаются — и когда?

2. Кэш-первый при получении (fetch)

Теперь, когда получено событие выборки, сервис-воркер может применить свою предпочтительную политику — здесь стратегия cache first означает, что сервис-воркер ищет совпадение в кэше и обращается к сети только в случае промаха. Обратите внимание, что для уточнения политики можно использовать предварительную фильтрацию по параметрам запроса — например, обращаться к кэшу только в том случае, если тип ресурса — HTML-страница и т. д.

Вот соответствующий фрагмент из нашего примера PWA.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// We have a cache-first strategy,
// where we look for resources in the cache first
// and only on the network if this fails.
self.addEventListener('fetch', event => {
    event.respondWith((async () => {
        const cache = await caches.open(CACHE_NAME);

        // Try the cache first.
        const cachedResponse = await cache.match(event.request);
        if (cachedResponse !== undefined) {
            // Cache hit, let's send the cached resource.
            return cachedResponse;
        } else {
            // Nothing in cache, let's go to the network.

            // ...... truncated ....
        }
    }
}

Обнаружение изменений в сети

В приведенных выше примерах были показаны сценарии, в которых политика кэширования не зависит от состояния сети (т.е. всегда проверяется сначала кэш). Но что если необходимо обусловить стратегию текущим состоянием сети?

Свойство navigator.onLine возвращает булево значение (true/false), отражающее сетевой статус браузера. В разных браузерах это свойство может быть реализовано по-разному, поэтому, чтобы избежать ложных срабатываний и отрицательных результатов, следует разобраться в нюансах. Свойство должно посылать события обновления при изменении статуса, и вы можете прослушивать эти события на уровне window.

1
2
3
4
5
6
window.addEventListener('online', function () {
    console.log('You are online!');
});
window.addEventListener('offline', function () {
    console.log('Oh no, you lost your network connection.');
});

Как же этот слушатель событий может уведомить сервис-воркер об этом изменении в сети? Сервис-воркеры и их клиенты могут взаимодействовать с помощью API postMessage. На стороне клиента это означает отправку сообщения с соответствующими данными:

1
2
3
4
navigator.serviceWorker.controller.postMessage({
    type: `IS_OFFLINE`,
    // add more properties if needed
});

На стороне сервис-воркера он прослушивает события message и распаковывает данные для выполнения дальнейших действий:

1
2
3
4
5
self.addEventListener('message', (event) => {
    if (event.data && event.data.type === 'IS_OFFLINE') {
        // take relevant actions
    }
});

Для получения информации о качестве сети можно также использовать свойство navigator.connection. К ним относятся:

  • navigator.connection.downlink для получения оценки эффективной пропускной способности (если она доступна)
  • navigator.connection.saveData, если пользователь активировал опцию "сохранить данные" в своем браузере

Визуальное обобщение

Сервис-воркеры могут выполнять и другие действия — например, обрабатывать push-уведомления или получать данные в фоновом режиме и использовать их для обновления приложения или кэша для повышения эффективности. Посмотрите материалы недели 4 (Платформы и практики), которые могут быть уместны в контексте.

Это было довольно много, не так ли? Надеюсь, это наглядное резюме (все, о чем мы говорили в разделе "Сервис-воркеры, варианты хранения и стратегии кэширования") поможет вам просмотреть и вспомнить ключевые элементы.

Иллюстрация к рассказу об автономной работе PWA

Учебные ресурсы

Работа с сервис-воркерами может быть непростой. Существует ряд ресурсов, которые стоит изучить самостоятельно, чтобы улучшить свое понимание и упростить работу разработчика.

  • Поддержка современных браузеров | Такие сайты, как caniuse.com и готов ли сервис-воркер?, предоставляют удобные для чтения панели, показывающие степень поддержки определенных API и функций сервис-воркера в различных браузерах.
  • Service Worker API | На этом MDN-ресурсе хорошо описаны интерфейсы сервис-воркеров, сценарии использования и связанные с ними API.
  • Storage Options | Подробнее о Cache Storage API, Cache API и IndexedDB API — чтобы понять, какие типы данных поддерживает каждый из них и как они могут быть использованы сервис-воркерами.
  • Workbox | Эти разработанные Google библиотеки обеспечивают работу готовых к производству сервисов-воркеров и описывают общие рецепты для стратегий кэширования, которые можно использовать в PWA.

В последующие недели мы будем ждать материалов, в которых некоторые из этих тем будут рассмотрены более подробно.

Упражнение: Ваша очередь!!!

Вы знаете, как это делается! Выберите Sample PWA и просмотрите его в браузере DevTools.

  • Откройте панель Сервис-воркера и просмотрите файл реализации.
  • Найдите обработчик события install — соотнесите его с содержимым кэша. Было ли что-нибудь предварительно кэшировано при запуске?
  • Найдите обработчик события fetch — какая стратегия кэширования применяется в PWA?
  • Включите режим "Offline" в панели сервис-воркера DevTools. Перейдите на разные страницы приложения. Что вы видите?
    • Появляется ли пользовательская автономная страница для некэшируемых ресурсов?
    • Получаются ли корректные страницы при переходе к предварительно кэшированным маршрутам?
  • Просмотрите остальную часть реализации сервис-воркера — постарайтесь понять, какие события он обрабатывает и как они влияют на стратегии кэширования и поведение в автономном режиме.

Источник — 1.5 Make PWA Work Offline

Комментарии