Original link:Developers.google.com/web/fundame…

Rich offline experiences, regular background syncing, and push notifications — these are usually features of mobile apps — but now they’re available on the Web as well. The Service Worker provides the technical foundation on which these functions depend.

What is a service worker?

A Service Worker is a script that the browser runs in the background, separate from the web page, opening the door to features that do not require web pages or user interaction. Today, they include features like push notifications and background syncing. In the future, Service workers may support other features, such as periodic synchronization or geo-fencing. The core capability discussed in this tutorial is the ability to intercept and process network requests, including programmatically managing the response cache.

It also has an exciting API that supports offline experiences, giving developers complete control over the experience.

Before the Service Worker, there was an API to provide users with an offline experience called AppCache. Service workers avoid many of the problems of the AppCache API.

Service Worker Considerations:

  • It is a JavaScript Worker, so it cannot access the DOM directly. However, the Service Worker can communicate with the pages it controls by responding to messages sent by the postMessage interface, which can manipulate the DOM as needed.
  • The Service Worker is a programmable network agent that allows you to control how network requests from pages are handled.
  • It terminates when it is not in use and restarts the next time it is needed, so it cannot rely on the Service Worker’sonfetchonmessageGlobal state in the handler. If some information needs to be retained and reused after a restart, the Service Worker can access itIndexedDB API.
  • Service workers make extensive use of promises, so if you’re not familiar with promises, learn about them first.

Service Worker life cycle

The life cycle of the Service Worker is completely separate from your web page.

To install the Service Worker, you need to register it in the JavaScript of the page.

Usually during the installation step, you need to cache some static resources. If all files are successfully cached, then the Service Worker has been successfully installed. If any files cannot be downloaded and cached, the installation step will fail and the Service Worker will not be activated (that is, not installed). If that happens, don’t worry, it will try again next time.

After installation, the activation step is performed, where the old cache management is handled, as described in the Service Worker Update section.

When activated, the Service Worker controls all pages within its scope, except pages that were first registered with the Service Worker until they are loaded again. Once a Service Worker is in control, it will be in one of two states: it will be terminated to save memory, or it will process fetch and Message events when network requests or messages are sent from the page.

Here is an overly simplified version of the Service Worker’s life cycle when first installed.

A prerequisite for

Browser support

Browser options are being added. Chrome, Firefox, and Opera support Service workers. Microsoft Edge now shows public support. Even Safari hints at things to come. You can follow the progress of all browsers at Jake Archibald’s Is Serviceworker Ready site.

HTTPS

During development, the Service Worker can be used with localhost, but to deploy it on the site, you need to set HTTPS on the server.

With Service workers, you can hijack connections, manufacture, and filter responses. A lot of power. While you will always have those powers, the middleman probably won’t. To avoid this, you can only register the Service Worker on the page where the Service is served over HTTPS, so we know if the Service Worker received by the browser has been tampered with.

GitHub Pages is a demo that provides services over HTTPS.

If you want to add HTTPS to the server, you need to get the TLS certificate and set it up on the server. Check the server’s documentation, and then Mozilla’s SSL configuration generator for action.

Registration Service Worker

Install the Service Worker by registering it on your page. This way the browser knows where your Service Worker is in the JavaScript file.

if ('serviceWorker' in navigator) {
    window.addEventListener('load'.function () {
        navigator.serviceWorker.register('/sw.js').then(function (registration) {
            // Registration was successful
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
        }, function (err) {
            // registration failed :(
            console.log('ServiceWorker registration failed: ', err);
        });
    });
}
Copy the code

This code checks if the Service Worker API is available and, if so, registers the Service Worker in /sw.js after the page loads.

You can call register() every time the page loads without worrying; The browser determines whether the service worker is registered and handles it accordingly.

One subtlety of the register() method is the location of the service worker file. In this case, you’ll notice that the service worker file is located at the root of the domain. This means that the scope of the service worker will be the entire source. In other words, the Service Worker will receive fetch events for all content on the domain. If we register the Service Worker in /example/sw.js, Then the Service Worker will only see the fetch event for pages whose URLS start with /example/ (i.e. /example/page1/, /example/page2/).

Now, you can jump to Chrome ://inspect/#service-workers and check if the service Worker is enabled on your site.

When implementing a Service Worker for the first time, use Chrome ://serviceworker-internals to view your Service Worker details. This url is useful only for understanding the life cycle of Service workers, and can be replaced by Chrome ://inspect/#service-workers.

You may find it useful to test the Service Worker in an invisible window, close and reopen the window, and the previous Service Worker does not affect the new window. Once the window is closed, any registrations and caches created from the incognito window are erased.

Install the service worker

After the registration process is started on the controlled page, the Service Worker script begins processing installation events.

Most basic example: You need to define callbacks for install events and decide which files to cache.

self.addEventListener('install'.function(event) {
  // Perform install steps
});
Copy the code

In the Install callback, you need to perform the following steps:

  1. Open cache,
  2. Cache our files,
  3. Verify that all necessary resources are cached.
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
  '/'.'/styles/main.css'.'/script/main.js'
];

self.addEventListener('install'.function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        returncache.addAll(urlsToCache); })); });Copy the code

As above, caches.open() passes in our cache name, followed by a call to cache.addall () passing in our array of files. This is a promise chain (caches. Open () and cache.addall ()). The event.waitUntil() method accepts this promise and uses it to know how long the installation will take and whether it was successful.

If all files are cached successfully, the Service Worker is successfully installed. If one of these files fails to download, the installation step fails. Therefore, be careful about the list of files cached during the installation step. Defining a long list of files increases the likelihood that a file will not be cached, causing the Service Worker to fail to install.

This is just a demo, so you can perform other tasks during installation events or avoid setting up installation event listeners entirely.

Cache and return requests

Now that we have the Service Worker installed, we need to return one of the cached responses.

After the Service Worker is installed and the user opens a new page or refreshes the current page, the Service Worker will start receiving fetch events, as shown in the following example.

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

Here we define the fetch event, and in event.respondwith () we pass in a promise from caches. Match (). This method looks at the request and looks for cached results from the cache created by the Service Worker.

If we have a matching response, the cached value is returned, otherwise the fetch call is returned;

If we want to accumulate a cache of new requests, we can do this by processing the response to the FETCH request and then adding it to the cache, as shown below.

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

        return fetch(event.request).then(
          function(response) {
            // Check if we received a valid response
            if(! response || response.status ! = =200|| response.type ! = ='basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have two streams.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            returnresponse; }); })); });Copy the code

The above code does the following:

  1. infetchOn the request to.then()Add a callback.
  2. After receiving the response, we perform the following checks:
    • Ensure that the response is valid.
    • Check that the status in the response is 200.
    • Ensure that the response type is BASIC, which proves that it is a request from our source, meaning that requests from third party resources are not cached.
  3. If it passes the check, the clone responds. And the reason for that is because the response is oneStream, sobodyCan only be used once. Since we want to return the response for use by the browser and pass it to the cache, we need to clone it so that we can send one to the browser and one to the cache.

Update Service Worker

Your Service Worker will need to be updated at some point. At this point, you need to perform the following steps:

  1. Update the Service Worker js file. When a user opens your site, the browser will try to re-download the script file that defines the Service Worker in the background. The Service Worker file is considered new if it is only one byte different from the current file.
  2. A new Service Worker will start and triggerinstallEvents.
  3. At this point, the old Service Worker is still controlling the current page, so the new Service Worker will enter the wait state.
  4. When the currently open page of the site closes, the old Service Worker is killed and the new Service Worker takes over.
  5. Once the new service worker gains control, itsactivateThe event will be triggered.

The activate callback does cache management. Why do you want to do this in the Activate callback? If any old caches are cleared during the installation step, any old Service workers controlling all current pages will suddenly stop serving files from that cache.

Suppose we have a cache called “my-site-cache-v1” that we want to split into a page cache and a blog post cache. This means that in the installation step, we will create two caches, “page-cache-v1” and “blog-posts-cache-v1”, and in the activation step, we will remove the old “my-site-cache-v1”.

The following code does this by iterating through all caches in the Service Worker and removing caches that are not defined in the cache license list.

self.addEventListener('activate'.function(event) {

  var cacheAllowlist = ['pages-cache-v1'.'blog-posts-cache-v1'];

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

Often on the pit

If the installation fails, no message is displayed

If the worker is registered but does not appear in chrome://inspect/#service-workers or Chrome ://serviceworker-internals, The installation may not be possible because of an error thrown or the Rejected Promise passed to event.waitUntil().

To solve this problem, Go to Chrome ://serviceworker-internals and select Open DevTools window and pause JavaScript execution on serviceworker startup For debugging “, and then interrupts the debugger at the start of the install event to locate the problem.

The default value of fetch()

By default, there are no credentials

When fetch is used, by default, the request does not contain credentials such as cookies. If credentials are required, call this:

fetch(url, {
  credentials: 'include'
})
Copy the code

Fetch behaves more like other CORS requests, such as , which never sends cookies unless you choose to use .

No CORS default failure

By default, fetching resources from third-party urls will fail if CORS is not supported. You can fix this by adding the no-Cors option to the request, which results in an “opaque” response, meaning that it is impossible to determine whether the response was successful or not.

cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
  return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
  console.log('All resources have been fetched and cached.');
});
Copy the code

Processing of responsive images

The srcset attribute or element selects the appropriate image resource at run time and sends a network request.

For Service workers, if you want to cache images in the install step, there are several options:

  1. The installation<picture>Elements andsrcsetProperty will be requested for all images;
  2. Install a single low resolution version of the image;
  3. Install a single high-resolution version of the image.

Actually, options 2 or 3 should be selected, but downloading all images wastes storage space.

Suppose you select the low resolution version at installation time, try to retrieve the high resolution image from the network when the page loads, and revert to the low resolution version if the high resolution image fails. That’s great, but there’s just one problem:

If we have the following two pictures:

The density of the screen Width Height
1x 400 400
2x 800 800

In the SRCset image, we will have some tags like this:

<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />
Copy the code

If on the 2X display, the browser will choose to download image-2x.png. Offline, if the image is cached, it can send a request in.catch() and return image-src.png, but the browser will take into account the extra pixels of the image on the 2X screen. So the image will be displayed as 200×200 CSS pixels instead of 400×400 CSS pixels. The only way to solve this problem is to set a fixed height and width on the image:

<img
  src="image-src.png"
  srcset="image-src.png 1x, image-2x.png 2x"
  style="width:400px; height: 400px;" />
Copy the code

Ok, when you see this, certify that you have read this article and give you a thumbs up. Please point out any improper translation.