If you’re looking for the ultimate Web experience, you’ve probably used PWA in your site, and you’ve probably faced some hesitation in writing Service Worker code because Service workers are so important that once registered in a user’s browser, The entire site’s requests are controlled by the Service Worker, and a small problem can become a big one. But now with Workbox 3, any worries about Service workers are no longer an issue.

The popular science Service Worker

If you are already familiar with Service workers, you can skip this section.

Service Worker is an important part of PWA, which is the brain of a website inserted into the user’s browser. A Service Worker is registered on a page like this:


     
  1. if ('serviceWorker' in navigator) {

  2.  navigator.serviceWorker.register('/sw.js')

  3. }

Copy the code

Why is SW the brain of a website? For example, if a SW is registered at the root of www.example.com, the SW can control all requests that the browser makes to www.example.com. By simply listening for fetch events, you can manipulate requests arbitrarily, return data read from CacheStorage, initiate new requests through the FETCH API, or even new a Response and return it to the page.

                        
     
  1. // A bad piece of SW code. After the SW is registered, all requests to the SW control site will return the string "bad", including the HTML of the page

  2. self .addEventListener( 'fetch', function(event ) {

  3. event.respondWith (

  4. new Response ('bad')

  5. );

  6. });

Copy the code

Because SW power is too big, it will be like walking on thin ice to write, accidentally some page resources can not be timely and correct update.

A fairly complete example of a Service Worker

Let’s start with a direct hand-written SW file


     
  1. var cacheStorageKey = 'cachesName';

  2. var cacheList = [

  3. // List of resources to cache immediately after successful registration

  4. ]

  5. // The install event is triggered when the browser finishes parsing the SW file

  6. self.addEventListener('install', function(e) {

  7. The install event typically requests the contents of the cacheList to be replaced into the Caches once, using the addAll method

  8.  e.waitUntil(

  9.    caches.open(cacheStorageKey).then(function(cache) {

  10.      return cache.addAll(cacheList)

  11.    })

  12.  );

  13. });

  14. // Triggers the Activate event when activated

  15. self.addEventListener('activate', function(e) {

  16. // Active events usually do some of the work of releasing expired resources, which are removed from caches when matched

  17.  var cacheDeletePromises = caches.keys().then(cacheNames => {

  18.    return Promise.all(cacheNames.map(name => {

  19. if (name ! == cacheStorageKey) {

  20.        return caches.delete(name);

  21.      } else {

  22.        return Promise.resolve();

  23.      }

  24.    }));

  25.  });

  26.  e.waitUntil(

  27.    Promise.all([cacheDeletePromises])

  28.  );

  29. });

  30. self.addEventListener('fetch', function(e) {

  31. // Write the cache policy here

  32.  e.respondWith(

  33. // Can be returned by matching resources in the cache

  34.    caches.match(e.request)

  35. // Can also be pulled from the remote end

  36.    fetch(e.request.url)

  37. // You can also build your own

  38. New Response(' make your own ')

  39. // Caches can also be placed with the caches. Put method

  40.  );

  41. });

Copy the code

In fact, all sites SW install and active are similar. They do nothing more than pre-cache resource list and cache cleaning after update. The logic is not too complicated, but the focus is on fetch events. In the above code, I have omitted the fetch event logic, because it would be too much to write carefully, and it would not be helpful to explain the cache policy. Imagine caches where you need to cache different resources with different file extensions, CSS, JS, HTML, images, all requiring a separate cache strategy. You can see how much you need to write in fetch.

Workbox 3

Workbox, defined as a collection of PWA-related tools, has emerged to address this problem, as well as a series of tools around it such as workbox-cli, gulp-workbox, webpack-workbox-plagin, and so on. But none of them are the focus of today’s talk, which is about Workbox itself.

Workbox can be thought of as Google’s official PWA framework, which solves the problem of writing PWA using the underlying API. The underlying API here refers to listening to the install, active and FETCH events of SW for corresponding logical processing, etc. It works like this:


     
  1. // Start with the Workbox framework

  2. ImportScripts (' https://storage.googleapis.com/workbox-cdn/releases/3.3.0/workbox-sw.js');

  3. workbox.precaching([

  4. // List of resources to cache immediately after successful registration

  5. ]);

  6. // HTML cache policy

  7. workbox.routing.registerRoute(

  8.  new RegExp(''.*\.html'),

  9.  workbox.strategies.networkFirst()

  10. );

  11. workbox.routing.registerRoute(

  12. new RegExp('.*\.(? :js|css)'),

  13.  workbox.strategies.cacheFirst()

  14. );

  15. workbox.routing.registerRoute(

  16.  new RegExp('https://your\.cdn\.com/'),

  17.  workbox.strategies.staleWhileRevalidate()

  18. );

  19. workbox.routing.registerRoute(

  20.  new RegExp('https://your\.img\.cdn\.com/'),

  21.  workbox.strategies.cacheFirst({

  22.    cacheName: 'example:img'

  23.  })

  24. );

Copy the code

It’s much easier to understand the code above, through workbox. Precaching fortress is install later into the contents of the caches of workbox. Routing. The first parameter is a regular in the registerRoute, The workbox. Strategies object provides us with several of the most commonly used strategies, as follows:

Stale-While-Revalidate

Cache First

Network First

Network Only

Cache Only

You can extend these policies via plugin, such as adding a cache expiration time (officially provided). You can even continue to listen for fetch events and then use these policies. The official documentation is here.

Rule of thumb

After a period of use and thinking, I think the most reasonable, the most conservative cache strategy.

HTML. If you want your page to be accessible offline, use NetworkFirst. If offline access is not required, use NetworkOnly.

CSS and JS, the situation is complicated, because the CSS and JS of general sites are in the CDN, SW has no way to determine whether the resources requested from the CDN are correct (HTTP 200), if the cache failed results, the problem will be big. In this case, I suggest using the stale-while-revalidate strategy to ensure the page speed. Even if the page fails, the user will update it after refreshing.

If your CSS, JS and site are in the same domain, and the file name has the Hash version number, you can use Cache First directly.

It is recommended to use Cache First and set a certain invalidation event. The request will not change once.

These are just general strategies, depending on your opinion.

Also, remember that Cache only and Cache first should never be used for any resource that is not in the same domain.

Make a final report

The Service Worker on the home page of Taobao PC has been online for a period of time. After constant adjustment of cache strategy, the benefits are still obvious. The total page download time drops from 1.7s to 1.4s on average, shortening the download time by nearly 18%.

In the previous example, we used Workbox introduced by Google’S CDN address. I have migrated version 3.3.0 to AlicDN and will continue to maintain and update it in the future. The usage method is as follows:


     
  1. ImportScripts (' https://g.alicdn.com/kg/workbox/3.3.0/workbox-sw.js');

  2. workbox.setConfig({

  3. ModulePathPrefix: 'https://g.alicdn.com/kg/workbox/3.3.0/'

  4. });

Copy the code

reference

  • Workbox official documentation

  • Fantastic Workbox 3.0

Photo by Peter Wendt on Unsplash