WorkBox

Admin Поддержка сайтов Обсудить

WorkBox — инструмент от Google для настройки service worker.

О том как настраивать service worker я уже писал.

Описанные методы в той статье помогают настроить сервис воркер, но при этом настраивая его вручную, придется самостоятельно решать все сложности с кэшем.

WorkBox это библиотека или если хотите фреймворк для PWA. Он помогает настроить service worker так, чтобы вы полностью контролировали всю работу с кэшем.

В этой статье будет дано описание как настроить сервис воркер версии 3.6.1. Текущая версия 5-ая и она работает чуть иначе. Однако и 3.6.1 справляется с этой задачей. А так как на другой версии я пока не создавал, опишу процесс на версии 3.6.1.

Настройка workbox

Скачиваем workbox и кладем его внутрь файла sw.js:

// Workbox from google
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("/")}}}();

Дальше в этом же файле внутрь конструкции:

if (workbox) {
}

Добавляем файлы, которые будут отвечать за режим офлайн и страницы 404:

    const versionPrecache = 1;
    workbox.precaching.precacheAndRoute([
        {
            "url": "pwa-offline.html",
            "revision": versionPrecache
        },
        {
            "url": "pwa-404.html",
            "revision": versionPrecache
        }
    ]);

Кэшируем изображения

Дальше есть несколько вариантов написания, что ложить в кэш, например:

    workbox.routing.registerRoute(
        /(.*)\.(?: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.

Стратегия networkFirst — это стратегия, при которой содержимое в кэше будет обновлено, если на сайте что-то изменилось. Если доступа в онлайн нет, то будет браться содержимое из кэша.

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

Указываем maxEntries — сколько хранить элементов в кэше и количество в секундах maxAgeSeconds.

Когда время указанное в maxAgeSeconds закончится, кэш удалиться, даже если приложение не в онлайн.

cacheableResponse — говорит о том, что сохранять только то, что отдаёт нам статус 200.

Кэшируем главную

const mainHandler = workbox.strategies.networkFirst({
        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.

По аналогии делаем для всего остального.

Принцип кэширования — кэширование идет сверху вниз. Если оно попадает в первые группы, то не попадает дальше.

Так как все страницы зачастую учесть сложно, я советую создать группу для всего и положить этот код в конец файла:

const allOtherHandler = workbox.strategies.networkFirst({
        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.

const DEBUG = true;

// Меняем версию файла, когда меняем 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 + " очищен");
    });
}

Файл минифест

О нем уже есть в этой статье. Надо повторить.

Привет! Ты находишься на моём сайте. Я разработчик. Здесь я делюсь своими наработками и знаниями. Спрашивай в комментариях, если тебе что-то не понятно или пиши, если есть что добавить.

Если вам пригодилась информация, вы можете поблагодарить автора сайта символическим пожертвованием:

Добавить комментарий

Напишите свой комментарий, если вам есть что добавить/поправить/спросить по теме текущей статьи:
"WorkBox"
Если вам нужно добавить участок кода ставьте его между тегами <code></code>