WorkBox

Admin Support

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"