Трудно понять, чем занимаются сервисные работники, не понимая их жизненного цикла. Их внутренняя работа покажется непрозрачной и даже произвольной. Полезно помнить, что, как и в случае с любым другим API браузера, поведение сервис-воркеров четко определено и специфицировано, что делает возможным работу автономных приложений, а также облегчает обновление без нарушения взаимодействия с пользователем.
Прежде чем погрузиться в Workbox, важно понять жизненный цикл сервис-воркера, чтобы то, что делает Workbox, имело смысл.
Определение терминов
Прежде чем перейти к жизненному циклу сервисного работника, стоит определить некоторые термины, касающиеся того, как работает этот жизненный цикл.
Контроль и объем
Идея контроля имеет решающее значение для понимания того, как работают работники сферы услуг. Страница, описанная как контролируемая сервисным работником, — это страница, которая позволяет сервисному работнику перехватывать сетевые запросы от ее имени. Сервисный работник присутствует и может выполнять работу со страницей в заданной области.
Объем
Область действия сервис-воркера определяется его расположением на веб-сервере. Если сервис-воркер запускается на странице, расположенной по адресу /subdir/index.html
и расположенной по адресу /subdir/sw.js
, областью действия сервис-воркера является /subdir/
. Чтобы увидеть концепцию области действия в действии, посмотрите этот пример:
- Перейдите по адресу https://service-worker-scope-viewer.glitch.me/subdir/index.html . Появится сообщение о том, что ни один сервисный работник не контролирует страницу. Однако на этой странице регистрируется сервисный работник из
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
. - Перезагрузите страницу. Поскольку работник службы зарегистрирован и теперь активен, он управляет страницей. Будет видна форма, содержащая область действия сервис-воркера, текущее состояние и его URL-адрес. Примечание. Необходимость перезагрузки страницы не имеет ничего общего с областью действия, а скорее с жизненным циклом сервис-воркера, который будет объяснен позже.
- Теперь перейдите по адресу https://service-worker-scope-viewer.glitch.me/index.html . Несмотря на то, что в этом источнике был зарегистрирован сервисный работник, по-прежнему появляется сообщение о том, что текущего сервисного работника нет. Это связано с тем, что эта страница не входит в область действия зарегистрированного сервисного работника.
Область действия ограничивает страницы, которыми управляет сервисный работник. В данном примере это означает, что сервис-воркер, загруженный из /subdir/sw.js
может управлять только страницами, расположенными в /subdir/
или его поддереве.
Выше описано, как определение области работает по умолчанию, но максимально допустимую область можно переопределить, установив заголовок ответа Service-Worker-Allowed
, а также передав параметр scope
методу register
.
Если нет веской причины ограничить область действия Service Worker подмножеством источника, загрузите Service Worker из корневого каталога веб-сервера, чтобы его область действия была как можно более широкой, и не беспокойтесь о Service-Worker-Allowed
заголовок. Так гораздо проще для всех.
Клиент
Когда говорят, что сервис-воркер контролирует страницу, на самом деле он контролирует клиента. Клиент — это любая открытая страница, URL-адрес которой попадает в область действия этого сервис-воркера. В частности, это экземпляры WindowClient
.
Жизненный цикл нового сервис-воркера
Чтобы работник службы мог управлять страницей, ее сначала необходимо, так сказать, создать. Начнем с того, что происходит, когда на веб-сайте развертывается новый сервис-воркер, на котором нет активного сервис-воркера.
Регистрация
Регистрация — это начальный этап жизненного цикла сервис-воркера:
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
Этот код выполняется в основном потоке и выполняет следующие действия:
- Поскольку первое посещение пользователем веб-сайта происходит без зарегистрированного сервисного работника, прежде чем регистрировать его, дождитесь полной загрузки страницы. Это позволяет избежать конфликтов за полосу пропускания, если сервисный работник что-либо предварительно кэширует.
- Хотя Service Worker хорошо поддерживается , быстрая проверка помогает избежать ошибок в браузерах, где он не поддерживается.
- Когда страница полностью загружена и если Service Worker поддерживается, зарегистрируйте
/sw.js
.
Вот некоторые ключевые вещи, которые следует понять:
- Сервис-воркеры доступны только через HTTPS или localhost .
- Если содержимое сервис-воркера содержит синтаксические ошибки, регистрация завершается неудачно, и сервис-воркер удаляется.
- Напоминание: работники службы работают в пределах области действия. Здесь областью действия является весь источник, поскольку он был загружен из корневого каталога.
- Когда начинается регистрация, состояние сервисного работника устанавливается на
'installing'
.
После завершения регистрации начинается установка.
Установка
Работник службы запускает событие install
после регистрации. install
вызывается только один раз для каждого сервис-воркера и не запускается снова, пока не будет обновлено. Обратный вызов для события install
можно зарегистрировать в области действия воркера с помощью addEventListener
:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
При этом создается новый экземпляр Cache
и выполняется предварительное кэширование ресурсов. Позже у нас будет много возможностей поговорить о предварительном кэшировании, поэтому давайте сосредоточимся на роли event.waitUntil
. event.waitUntil
принимает обещание и ждет, пока оно не будет выполнено. В этом примере это обещание выполняет две асинхронные операции:
- Создает новый экземпляр
Cache
с именем'MyFancyCache_v1'
. - После создания кеша массив URL-адресов ресурсов предварительно кэшируется с помощью асинхронного метода
addAll
.
Установка завершается неудачно, если обещания, переданные в event.waitUntil
отклонены . Если это произойдет, сервисный работник будет удален.
Если промисы разрешаются , установка завершается успешно, состояние сервис-воркера изменится на 'installed'
и затем активируется.
Активация
Если регистрация и установка прошли успешно, сервис-воркер активируется, и его состояние становится 'activating'
Работа может быть выполнена во время активации в событии activate
сервис-воркера. Типичной задачей в этом случае является удаление старых кэшей, но для совершенно нового сервис-воркера это на данный момент неактуально и будет расширено, когда мы будем говорить об обновлениях сервис-воркера.
Для новых сервисных работников activate
пожары сразу после успешной install
. После завершения активации состояние сервисного работника становится 'activated'
. Обратите внимание, что по умолчанию новый сервисный работник не начнет управлять страницей до следующей навигации или обновления страницы.
Обработка обновлений сервис-воркера
После развертывания первого сервисного работника его, скорее всего, потребуется обновить позже. Например, обновление может потребоваться, если происходят изменения в логике обработки запросов или предварительного кэширования.
Когда происходят обновления
Браузеры будут проверять наличие обновлений у сервис-воркера, когда:
- Пользователь переходит на страницу в области действия сервис-воркера.
-
navigator.serviceWorker.register()
вызывается с URL-адресом, отличным от URL-адреса установленного в данный момент сервис-воркера , но не меняйте URL-адрес сервис-воркера ! -
navigator.serviceWorker.register()
вызывается с тем же URL-адресом, что и установленный сервис-воркер, но с другой областью действия. Опять же, избегайте этого, по возможности сохраняя область видимости в корне источника. - Когда такие события, как
'push'
или'sync'
были вызваны в течение последних 24 часов — но пока не беспокойтесь об этих событиях.
Как происходят обновления
Знать, когда браузер обновляет сервис-воркера, важно, но важно и то, «как». Предполагая, что URL-адрес или область действия Service Worker не изменились, установленный в данный момент Service Worker обновляется до новой версии только в том случае, если его содержимое изменилось.
Браузеры обнаруживают изменения несколькими способами:
- Любые побайтовые изменения в сценариях, запрошенные
importScripts
, если применимо. - Любые изменения в коде верхнего уровня сервис-воркера, влияющие на отпечаток пальца, сгенерированный браузером.
Браузер выполняет здесь большую тяжелую работу. Чтобы гарантировать, что у браузера есть все необходимое для надежного обнаружения изменений в содержимом сервис-воркера, не указывайте HTTP-кешу хранить его и не меняйте имя его файла. Браузер автоматически выполняет проверку обновлений при переходе на новую страницу в области действия сервис-воркера.
Ручной запуск проверок обновлений
Что касается обновлений, то логика регистрации вообще не должна меняться. Тем не менее, одним исключением может быть ситуация, когда сеансы на веб-сайте долговечны. Это может произойти в одностраничных приложениях, где запросы навигации встречаются редко, поскольку приложение обычно встречает один запрос навигации в начале жизненного цикла приложения. В таких ситуациях обновление вручную может быть запущено в основном потоке:
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
Для традиционных веб-сайтов или в любом случае, когда пользовательские сеансы не являются продолжительными, запуск обновлений вручную, вероятно, не требуется.
Установка
При использовании сборщика для создания статических ресурсов эти ресурсы будут содержать хеши в своем имени, например framework.3defa9d2.js
. Предположим, что некоторые из этих ресурсов предварительно кэшируются для последующего автономного доступа. Для этого потребуется обновление Service Worker для предварительного кэширования обновленных ресурсов:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
Две вещи отличаются от первого примера события install
, приведенного ранее:
- Создается новый экземпляр
Cache
с ключом'MyFancyCacheName_v2'
. - Названия предварительно кэшированных ресурсов изменились.
Следует отметить, что обновленный сервисный работник устанавливается вместе с предыдущим. Это означает, что старый сервис-воркер по-прежнему контролирует все открытые страницы, а после установки новый переходит в состояние ожидания, пока не будет активирован.
По умолчанию новый сервис-воркер активируется, когда старый не контролирует ни одного клиента. Это происходит, когда все открытые вкладки соответствующего веб-сайта закрыты.
Активация
Когда обновленный Service Worker установлен и фаза ожидания заканчивается, он активируется, а старый Service Worker удаляется. Обычной задачей, выполняемой в событии activate
обновленного сервис-воркера, является очистка старых кэшей. Удалите старые кеши, получив ключи для всех открытых экземпляров Cache
с помощью caches.keys
и удалив кеши, которые не входят в определенный список разрешений, с помощью caches.delete
:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
Старые тайники не приводят в порядок. Нам нужно сделать это самим, иначе мы рискуем превысить квоты на хранение . Поскольку 'MyFancyCacheName_v1'
от первого сервисного работника устарел, список разрешений кэша обновляется, чтобы указать 'MyFancyCacheName_v2'
, который удаляет кеши с другим именем.
Событие activate
завершится после удаления старого кэша. На этом этапе новый сервис-воркер возьмет на себя управление страницей, окончательно заменив старую!
Жизненный цикл продолжается постоянно
Независимо от того, используется ли Workbox для управления развертыванием и обновлением Service Worker или API Service Worker напрямую, полезно понимать жизненный цикл Service Worker. При таком понимании поведение сервисных работников должно показаться скорее логичным, чем загадочным.
Тем, кто хочет глубже погрузиться в эту тему, стоит прочитать эту статью Джейка Арчибальда . Существует множество нюансов в том, как проходит весь жизненный цикл службы, но их можно понять, и эти знания будут иметь большое значение при использовании Workbox.