WorkBox — инструмент от Google для настройки service worker.
О том как настраивать service worker я уже писал.
Описанные методы в той статье помогают настроить сервис воркер, но при этом настраивая его вручную, придется самостоятельно решать все сложности с кэшем.
WorkBox это библиотека или если хотите фреймворк для PWA. Он помогает настроить service worker так, чтобы вы полностью контролировали всю работу с кэшем.
В этой статье будет дано описание как настроить сервис воркер версии 3.6.1. Текущая версия 5-ая и она работает чуть иначе. Однако и 3.6.1 справляется с этой задачей. А так как на другой версии я пока не создавал, опишу процесс на версии 3.6.1.
Настройка workbox
Скачиваем workbox и кладем его внутрь файла sw.js:
var workbox=function(){"use strict";try{self.workbox.v["workbox:sw:3.6.1"]=1}catch(t){}const t="https://storage.googleapis.com/workbox-cdn/releases/3.6.1",e={backgroundSync:"background-sync",broadcastUpdate:"broadcast-cache-update",cacheableResponse:"cacheable-response",core:"core",expiration:"cache-expiration",googleAnalytics:"google-analytics",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams"};return new class{constructor(){return this.v={},this.t={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.e=this.t.debug?"dev":"prod",this.s=!1,new Proxy(this,{get(t,s){if(t[s])return t[s];const o=e[s];return o&&t.loadModule(`workbox-${o}`),t[s]}})}setConfig(t={}){if(this.s)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.t,t),this.e=this.t.debug?"dev":"prod"}skipWaiting(){self.addEventListener("install",()=>self.skipWaiting())}clientsClaim(){self.addEventListener("activate",()=>self.clients.claim())}loadModule(t){const e=this.o(t);try{importScripts(e),this.s=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}o(e){if(this.t.modulePathCb)return this.t.modulePathCb(e,this.t.debug);let s=[t];const o=`${e}.${this.e}.js`,r=this.t.modulePathPrefix;return r&&""===(s=r.split("/"))[s.length-1]&&s.splice(s.length-1,1),s.push(o),s.join("/")}}}();
Дальше в этом же файле внутрь конструкции:
}
Добавляем файлы, которые будут отвечать за режим офлайн и страницы 404:
workbox.precaching.precacheAndRoute([
{
"url": "pwa-offline.html",
"revision": versionPrecache
},
{
"url": "pwa-404.html",
"revision": versionPrecache
}
]);
Кэшируем изображения
Дальше есть несколько вариантов написания, что ложить в кэш, например:
/(.*)\.(?:png|gif|jpg)(.*)/,
workbox.strategies.networkFirst({
cacheName: 'images',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
}),
new workbox.expiration.Plugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 7,
purgeOnQuotaError: true
})
]
})
);
Этим мы создали группу images, воспользовались стратегией networkFirst.
Используем purgeOnQuotaError, чтобы при превышении квоты на хранение данных очищать содержимое кэша.
Указываем maxEntries — сколько хранить элементов в кэше и количество в секундах maxAgeSeconds.
cacheableResponse — говорит о том, что сохранять только то, что отдаёт нам статус 200.
Кэшируем главную
cacheName: 'main',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
}),
new workbox.expiration.Plugin({
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 3,
purgeOnQuotaError: true
})
]
});
workbox.routing.registerRoute(/^https:\/\/ploshadka.(net|net\/)$/, args => {
return mainHandler.handle(args).then(response => {
if (!response) {
return caches.match('pwa-offline.html');
} else if (response.status === 404) {
return caches.match('pwa-404.html');
}
return response;
});
});
В этой записи мы говорим о том, что главную страницу кладем в отдельную группу. Если она не закэширована, будем показывать страницу офлайн — pwa-offline.html.
По аналогии делаем для всего остального.
Принцип кэширования — кэширование идет сверху вниз. Если оно попадает в первые группы, то не попадает дальше.
Так как все страницы зачастую учесть сложно, я советую создать группу для всего и положить этот код в конец файла:
cacheName: 'other',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
}),
new workbox.expiration.Plugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24,
purgeOnQuotaError: true
})
]
});
workbox.routing.registerRoute(/(.*)/, args => {
return allOtherHandler.handle(args).then(response => {
if (!response) {
return caches.match('pwa-offline.html');
} else if (response.status === 404) {
return caches.match('pwa-404.html');
}
return response;
});
});
Тогда любая страница будет показывать в режиме офлайн страницу pwa-offline.html.
Установка Service Worker
Нам понадобится еще один файл, который будет устанавливать все то что мы написали выше.
Назовем этот файл sw-setup.js.
// Меняем версию файла, когда меняем service worker
const serviceWorkerVer = "/sw.js?v=1";
var url = window.location.origin,
fullUrl = url + serviceWorkerVer;
+function installServiceWorker() {
if ("serviceWorker" in navigator) {
window.addEventListener('load', () => {
if (navigator.serviceWorker.controller !== null) {
DEBUG && console.log("[SW] Текущая версия в браузере " + navigator.serviceWorker.controller.scriptURL);
DEBUG && console.log("[SW] Новая версия " + fullUrl);
}
if (navigator.serviceWorker.controller === null) {
registrationServiceWorker(navigator.serviceWorker);
} else if (
navigator.serviceWorker.controller.scriptURL !== fullUrl) {
removeServiceWorker(navigator.serviceWorker);
removeCache('workbox-precache-v2-' + url + '/');
removeCache('workbox-precache-' + url + '/');
removeCache('workbox-precache-' + url + '/-temp');
removeCache('workbox-precache');
removeCache('images');
removeCache('main');
removeCache('other');
} else {
DEBUG && console.log("[SW] Активный service worker последней версии найден, повторно не регистрируем");
}
});
}
}();
// Регистрация Service Worker
function registrationServiceWorker(navigatorServiceWorker) {
navigatorServiceWorker
.register(serviceWorkerVer)
.then(reg => {
DEBUG && console.log(`[SW] Зарегистрирован service worker для адреса (scope): ${reg.scope}`);
})
.catch(err => {
DEBUG && console.log(`[SW] При регистрации service worker произошла ошибка: ${err}`);
});
}
// Удаления service worker
function removeServiceWorker(navigatorServiceWorker) {
navigatorServiceWorker.getRegistrations()
.then(function (registrations) {
for (var registration of registrations) {
registration.unregister();
DEBUG && console.log("[SW] Предыдущая версия service worker была успешно удалена");
DEBUG && console.log("[SW] ! Для установки новой версии перезагрузите страницу");
}
}
);
}
// Очистка кэша
function removeCache(cache) {
caches.delete(cache).then(function (boolean) {
DEBUG && console.log("[SW] Кэш " + cache + " очищен");
});
}
Файл минифест
О нем уже есть в этой статье. Надо повторить.