What is theService Worker

The Service Worker essentially acts as a proxy server between the Web application and the browser, and can also act as a proxy between the browser and the network when the network is available. They are designed, among other things, to enable the creation of an effective offline experience, intercept network requests and take appropriate action based on whether the network is available and whether updated resources reside on the server. They also allow access to push notifications and background synchronization apis.

  • Service WorkerThe essence of aWeb WorkerTheta is independent of thetaJavaScriptMain thread, so it cannot be accessed directlyDOM, and cannot be accessed directlywindowObject, however,Service WorkerYou can visitnavigatorObject, which can also be passed through messaging (postMessage) andJavaScriptThe main thread communicates.
  • Service WorkerIs a network agent, it can controlWebAll network requests for the page.
  • Service WorkerHas its own life cycle, use wellService WorkerThe key is flexible control of its life cycle.

Service WorkerThe role of

  • For browser caching
  • To realize the offlineWeb APP
  • Being pushed

Service Workercompatibility

Service Worker is an advanced feature of modern browsers. It relies on fetch API, Cache Storage, Promise, etc. Among them, Cache provides the Storage mechanism of Request/Response object pairs. Cache Storage Stores multiple caches.

The sample

Before we understand how a Service Worker works, let’s look at an example of a Service Worker:

self.importScripts('./serviceworker-cache-polyfill.js');

var urlsToCache = [
  '/'.'/index.js'.'/style.css'.'/favicon.ico',];var CACHE_NAME = 'counterxing';

self.addEventListener('install'.function(event) {
  self.skipWaiting();
  event.waitUntil(
    caches.open(CACHE_NAME)
    .then(function(cache) {
      returncache.addAll(urlsToCache); })); }); self.addEventListener('fetch'.function(event) {
  event.respondWith(
    caches.match(event.request)
    .then(function(response) {
      if (response) {
        return response;
      }
      returnfetch(event.request); })); }); self.addEventListener('activate'.function(event) {
  var cacheWhitelist = ['counterxing'];

  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === - 1) {
            returncaches.delete(cacheName); }})); })); });Copy the code

Here is a paragraph-by-paragraph analysis to demystify Service workers:

polyfill

Look at the first line: self.importScripts(‘./serviceworker-cache-polyfill.js’); , which introduces a polyfill of the Cache API. This polyfill support makes the Cache Storage API available in older browsers. In order to realize the function of Service Worker, it is usually necessary to match the network request of Cache API proxy to the Cache.

In the Service Worker thread, polyfill scripts are introduced using importScripts for compatibility with older browsers.

Cache Resources List And Cache Name

We then use a list of urlsToCache to declare static resources to be cached. We use a variable CACHE_NAME to determine the Name of the Cache Storage that is currently being cached.

var urlsToCache = [
  '/'.'/index.js'.'/style.css'.'/favicon.ico',];var CACHE_NAME = 'counterxing';
Copy the code

Lifecycle

The Service Worker is independent of the browser JavaScript main thread and has its own independent life cycle.

If you need to install a Service Worker on your website, you need to introduce the Service Worker in the JavaScript main thread using the following code.

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    console.log('Successful installation', registration.scope);
  }).catch(function(err) {
    console.log(err);
  });
}
Copy the code

It is important to note here that the path of the sw.js file, in my example, is in the current domain root, which means that the Service Worker and the site are homologous and can be used as a proxy for all requests to the current site. If the Service Worker is registered under /imaging/sw.js, That can only represent network requests under /imaging.

You can use the Chrome console to view the Service Worker status on the current page:

After installation, the Service Worker goes through the following lifecycle:

  1. Download (download)
  2. Installation (install)
  3. Activation (activate)
  • The first time a user accesses a site or page controlled by the Service Worker, the Service Worker is downloaded immediately. It will be downloaded at least every 24 hours after that. It may be downloaded more frequently, but it must be downloaded every 24 hours to avoid bad scripts working for too long.

  • After the download completes, the installation of the Service Worker begins, and during the installation phase, it is usually necessary to cache some static resources that we have pre-declared, in our case through urlsToCache.

  • The browser will try to download the Service Worker script file. After the download is successful, the browser will compare it with the previously cached Service Worker script file. If it is different from the previous Service Worker script file, Verify that the Service Worker has been updated, triggering the activate event. The activation is complete.

The approximate life cycle of the Service Worker is shown in the figure:

install

After the installation is complete, try caching some static resources:

self.addEventListener('install'.function(event) {
  self.skipWaiting();
  event.waitUntil(
    caches.open(CACHE_NAME)
    .then(function(cache) {
      returncache.addAll(urlsToCache); })); });Copy the code

First, the execution of self.skipWaiting() tells the browser to skip the waiting phase, eliminate the outdated sw.js Service Worker script, and start trying to activate the new Service Worker.

Then open a Cache with caches. Open. Once open, try caching our pre-declared static file with cache.addall.

Listening to thefetch, proxy network request

All network requests of the page will be triggered by the Service Worker’s FETCH event. The Service Worker tries to find the Cache from the Cache through caches. Match. Create a real network request.

self.addEventListener('fetch'.function(event) {
  event.respondWith(
    caches.match(event.request)
    .then(function(response) {
      if (response) {
        return response;
      }
      returnfetch(event.request); })); });Copy the code

If we need to add a new Cache to the Cache Storage during the request, we can use the cache.put method to add a new Cache to the Cache Storage.

self.addEventListener('fetch'.function(event) {
  event.respondWith(
    caches.match(event.request)
    .then(function(response) {
      // Cache hit
      if (response) {
        return response;
      }

      // Note that the request must be cloned using the clone method
      // The reason is that response is a Stream, in order to make the browser and cache use this response
      // You must clone the response, one to the browser and one to the cache.
      // Can only be consumed once. To be consumed again, you must clone it once
      var fetchRequest = event.request.clone();

      return fetch(fetchRequest).then(
        function(response) {
          // It must be a valid request. It must be a same-origin response, a third-party request, because it is not controllable, it is best not to cache
          if(! response || response.status ! = =200|| response.type ! = ='basic') {
            return response;
          }

          // Once consumed, clone again
          var responseToCache = response.clone();
          caches.open(CACHE_NAME)
            .then(function(cache) {
              cache.put(event.request, responseToCache);
            });
          returnresponse; }); })); });Copy the code

In a project, it is important to control caching, which is generally not recommended for interface requests. So in my own project, I didn’t do a dynamic caching scheme here.

activate

There will always be a day when Service workers need to be updated. With version iteration, one day, we need to release the functions of the new version online, at this time, we need to eliminate the old Cache. How to eliminate the old Service Worker and Cache Storage?

self.addEventListener('activate'.function(event) {
  var cacheWhitelist = ['counterxing'];

  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === - 1) {
            returncaches.delete(cacheName); }})); })); });Copy the code
  1. First of all, there’s a whitelistCacheIs not eliminated.
  2. After throughcaches.keys()Get all of themCache Storage, will not be in the whitelistCacheObsolete.
  3. replacingcaches.delete()Methods. It receivescacheNameAs a parameter, delete itcacheNameAll caches.

sw-precache-webpack-plugin

Sw-precach-webpack-plugin is a Webpack plugin that can be configured to generate the desired sw.js Service Worker script when webpack is packaged.

The simplest configuration is as follows:

var path = require('path');
var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');

const PUBLIC_PATH = 'https://www.my-project-name.com/';  // webpack needs the trailing slash for output.publicPath

module.exports = {

  entry: {
    main: path.resolve(__dirname, 'src/index'),},output: {
    path: path.resolve(__dirname, 'src/bundles/'),
    filename: '[name]-[hash].js'.publicPath: PUBLIC_PATH,
  },

  plugins: [
    new SWPrecacheWebpackPlugin(
      {
        cacheId: 'my-project-name'.dontCacheBustUrlsMatching: /\.\w{8}\./.filename: 'service-worker.js'.minify: true.navigateFallback: PUBLIC_PATH + 'index.html'.staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
      }
    ),
  ],
}
Copy the code

After performing webpack packing, a file named service-worker.js is generated to cache static webpack files.

The simplest example.

Service Worker Cache VS Http Cache

Compared to Http Header caching, Service workers with Cache Storage also have their own advantages:

  1. Cache and update coexist: each update version, with the help ofService WorkerYou can use the cache to return immediately, but in the meantime you can make a request to verify if there is a new version update.
  2. Non-invasive:hashThe value is really ugly.
  3. Not easily washed off:HttpCaches are easy to flush and expire, whileCache StorageThey’re less likely to wash off. There’s no expiration date.
  4. Offline: With helpService WorkerOffline access to applications can be achieved.

However, the disadvantage is that Service workers are not compatible because they rely on fetch API, Promise, Cache Storage, etc.

The latter

This article only briefly summarizes the basic use of Service Worker and the simple way to use Service Worker to do client caching. However, the function of Service Worker is far more than this, for example: Use the Service Worker for offline applications, notifications for web applications (see push-notifications), etc.

You can even use the Service Worker to cache the interface, which is not that complicated in my project. However, the advantage of the interface cache is that it supports offline access and can access our Web application normally in offline state.

Cache Storage and Service workers are always inseparable. The best use of a Service Worker is offline caching with a Cache Storage. With the help of Service workers, network requests can be easily controlled and different strategies can be adopted for different network requests. For Cache policies, for example, there are many cases. For example, you can preferentially use network requests, and then use cache when network requests fail. You can also use cache and network requests at the same time. On the one hand, you can check the request, on the other hand, you can check the cache, and then see which one is faster.