Service Worker

Кеширование

Регистрация


if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('service-worker.js')
     .then((registration) => {
          // success
      })
      .catch((error) => {
          // error
      });
}

service-worker.js

npm i serviceworker-webpack-plugin --save-dev

new ServiceWorkerWebpackPlugin({
  entry: root('src/app/service-worker'),
  excludes: ['sw.*.js', 'online.html'],
  includes: ['**/*.css', '**/*.js', '**/*.json', '**/*.png', '**/*.svg']
});
import runtime from "serviceworker-webpack-plugin/lib/runtime.js";

// Register SW
if ("serviceWorker" in navigator) {
  runtime.register();
}

Стратегии кеширования

  • Cache-first для статики
  • Network-first для API
  • Только GET-запросы и другие органичения
self.addEventListener('fetch', (event) => {
  const request = event.request;
   
  if (!shouldHandle(request)) {
      event.respondWith(fetch(request));
      return;
  }

  if (isStatic(request)) {
      // cache-first
      event.respondWith(
          fetchFromCache(event)
          .catch(() => fetch(request))
          .then(response => addToCache(cacheKey, request, response))
          .catch(() => offlineResponse(resourceType, opts))
      );
  } else {
      // network-first
      event.respondWith(
          fetch(request)
          .then(response => addToCache(cacheKey, request, response))
          .catch(() => fetchFromCache(event))
          .catch(() => offlineResponse(opts))
      );
  }

});
function isStatic(request) {
  const path = getPath(request.url);
  return serviceWorkerOption.assets.some(p => p === path);
}

Демо

Нюансы

Офлайн режим

window.addEventListener('online', 
    () => alert('online!'));  

window.addEventListener('offline', 
    () => alert('offline!'));

sw.js

event.respondWith(
  fetch(request)
      .then(response => broadcastOnline())
      .catch(() => broadcastOffline())
 );
function broadcastOnline() {
  clients.matchAll().then(clients => {
      clients.forEach(client => {
          client.postMessage({action: 'onlineStatus', online: true});
      });
  });
}

App

window.addEventListener('offline', () => notifyOffline());

window.addEventListener('online', () => {
  fetch("online.html");
});

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.addEventListener('message', (event) => {
      switch(event.data.action) {
          case 'onlineStatus':
              event.data.online ? notifyOnline() : notifyOffline();
              break;
      }
  });
}

Code splitting

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.addAll(
        [
          '/css/bootstrap.css',
          '/css/main.css',
          '/js/bootstrap.min.js',
          '/js/jquery.min.js',
          '/offline.html'
        ]
      );
    })
  );
});

Очистка кеша

caches.keys().then((keyList) => {
    keyList.map((key, i) => {
        if (key !== appCachenName) {
            caches.delete(keyList[i])
        }
    })
})

operation is insecure

set(key: Request | string, value: Response): Promise {
    if (!isSupport()) {
        return Promise.resolve();
    }
    return caches.open(this.cacheNAme)
        .then(cache => cache.put(key, value))
        // avoid security errors
        .catch(() => { /* do nothing */ });
}

Cache then network

Cache then network

var networkDataReceived = false;

startSpinner();

// fetch fresh data
var networkUpdate = fetch('/data.json').then((response) => {
  return response.json();
}).then((data) => {
  networkDataReceived = true;
  updatePage(data);
});

// fetch cached data
caches.match('/data.json').then(function(response) {
  if (!response) throw Error("No data");
  return response.json();
}).then(function(data) {
  // don't overwrite newer network data
  if (!networkDataReceived) {
    updatePage(data);
  }
}).catch(function() {
  // we didn't get cached data, the network is our last hope:
  return networkUpdate;
}).catch(showErrorMessage).then(stopSpinner());

Cache then network

+ "Нулевая" задержка

+ Относительно низкая стоимость

Демо

Вопросы?

sw

By Viacheslav Bukharin