Workbox has recently released a large version of Workbox, from v3.x to v4.x.


What is a workbox – the window?

The Workbox-Window package is a set of modules used to run in the window context, that is, inside your web page. They complement the other workboxes running in servicewoker.

The main features/goals of Workbox-Window are:

  • By helping developers determineserviceWorkerThe most critical moments in the life cycle, and simplify the response to those moments, simplifyserviceWokerRegistration and renewal process.
  • Helps prevent developers from making the most common mistakes.
  • makeserviceWorkerThe code running in the program andwindowEasier communication between code running in.

Import and use workbox-window

The main entry point for the Workbox-Window package is the WorkBox class, which you can import into your code from our CDN or using any of the popular JavaScript packaging tools.

Use our CDN

The easiest way to import the Workbox class on your site is from our CDN:

<script type="module">
import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/4.0.0/workbox-window.prod.mjs';

if ('serviceWorker' in navigator) {
  const wb = new Workbox('/sw.js');

  wb.register();
}
</script>
Copy the code

Note that this example uses

All major browsers that support serviceWorker also support JavaScript modules, so providing this code to any browser is perfect (older browsers will ignore it).

Load Workbox with JavScript package

While tools are absolutely not required to use Workbox, if your development infrastructure already includes packaging tools such as WebPack or Rollup for use with NPM dependencies, you can use them to load Workbox.

The first step is to install Workbox as your app’s dependency:

npm install workbox-window
Copy the code

Then, in the JavaScript files of one of your applications, import workbox by referring to the workbox-window package name:

import {Workbox} from 'workbox-window';

if ('serviceWorker' in navigator) {
  const wb = new Workbox('/sw.js');

  wb.register();
}
Copy the code

If your packaging tool supports code splitting through dynamic import statements, you can also conditionally load the Workbox-Window, which helps reduce the size of the page main package.

Although Workbox is very small (1KB gzip compressed), there is no reason to load the core application logic of the site because serviceWorker is progressively enhanced in nature.

if ('serviceWorker' in navigator) {
  const {Workbox} = await import('workbox-window');

  const wb = new Workbox('/sw.js');
  wb.register();
}
Copy the code

Advanced packaging concepts

Unlike the Workbox package running in the Service worker, the build files referenced by the workbox-window in the main and module fields in package.json are converted to ES5. This makes them compatible with today’s build tools – some of which don’t allow developers to transform anything about their node_module dependencies.

If your build system allows you to convert dependencies (or if you don’t need to convert any code), it’s best to import specific source files rather than the package itself.

Here are the various methods you can import Workbox with a description of what each method will return:

Import the UMD version using ES5 syntax
// (pkg.main: "build/workbox-window.prod.umd.js")
const {Workbox} = require('workbox-window');

// Import the module version using ES5 syntax
// (pkg.module: "build/workbox-window.prod.es5.mjs")
import {Workbox} from 'workbox-window';

// Import the module source file using ES2015 + syntax
import {Workbox} from 'workbox-window/Workbox.mjs';
Copy the code

Important! If you import the source file directly, you also need to configure the build process to shrink the file and remove development-only code when you deploy it to production. For more information, see the guide to using Packaging (Webpack/Rollup) and Workbox.

The sample

Once you import the Workbox class, you can use it to register and interact with serviceWorkers. Here are some examples of how you can use Workbox in your applications:

Register the serviceWorker and notify the user the first time the serviceWorker is active:

Many Web application users, serviceWorker, pre-cache resources so that their application works offline when subsequent pages load. In some cases, it makes sense to notify the user that the application is now available offline.

const wb = new Workbox('/sw.js');

wb.addEventListener('activated', (event) => {
  // For another version of serviceWorker, 'event.isUpdate' will be true
  // When this version is registered, the worker is controlling the page.
  if(! event.isUpdate) {console.log('Service worker activated for the first time! ');

    // If your serviceWorker is configured to pre-cache resources, then
    // All resources should now be available.}});// Register the serviceWorker after adding event listeners.
wb.register();
Copy the code

Notify the user if serviceWorker is installed but waiting to be activated

When a new serviceWorker is registered on a page controlled by an existing serviceWorker, by default the serviceWorker is not activated until all clients controlled by the original serviceWorker are completely uninstalled.

This is a common source of confusion for developers, especially if reloading the current page does not cause a new serviceWorker program to activate.

To help reduce confusion and make it clear when this happens, the Workbox class provides a wait event that can be listened on:

const wb = new Workbox('/sw.js');

wb.addEventListener('waiting', (event) => {
  console.log('A new serviceWorker was installed but could not activate' +
      'until all the tabs running the current version are completely unloaded. `);
});

// Register the service worker after adding event listeners.
wb.register();
Copy the code

Notify the user of cache updates from the workbox-broadcast-update package

The workbox-broadcast-Update package is great

The ability to provide content from the cache (fast delivery) while also notifying users of updates to that content (using the stale-while-revalidate policy).

To receive these updates from the window, you can listen for a message event of type CACHE_UPDATE:

const wb = new Workbox('/sw.js');

wb.addEventListener('message', (event) => {
  if (event.data.type === 'CACHE_UPDATE') {
    const {updatedURL} = event.data.payload;

    console.log(`${updatedURL}A newer version is available); }});// Register the service worker after adding event listeners.
wb.register();
Copy the code

Send the serviceWorker a list of urls to cache

For some applications, you know all the resources that need to be pre-cached at build time, but some applications provide completely different pages based on the URL the user first logs in to.

In the latter category, it might make sense to cache only the resources needed for a particular page that the user is visiting. With the Workbox-routing package, you can send a list of urls to the router for caching, and it will cache these urls according to the rules defined by the router itself.

This example sends a list of page-loaded urls to the router each time a new serviceWorker is activated. Note that it is ok to send all urls, because only urls that match routes defined in serviceWorker are cached:

const wb = new Workbox('/sw.js');

wb.addEventListener('activated', (event) => {
  // Get the current page URL + all the resources loaded on the page.
  const urlsToCache = [
    location.href,
    ...performance.getEntriesByType('resource').map((r) = > r.name),
  ];
  // Send the list of urls to the serviceWorker's router.
  wb.messageSW({
    type: 'CACHE_URLS'.payload: {urlsToCache},
  });
});

// Register the serviceWorker after adding event listeners.
wb.register();
Copy the code

Note: the above technology applied to on by default router workbox. Routing. RegisterRoute () method to define any routing. If you want to create your own router instance, you need to manually call addCacheListener().

Important serviceWorker lifecycle

The life cycle of a serviceWorker is understandably complex. Part of what makes it so complex is that it has to deal with all the edge cases that serviceWorker might use (for example, registering multiple Serviceworkers, registering different Serviceworkers in different frameworks, Register serviceworkers with different names, etc.).

But most developers implementing serviceWorker shouldn’t worry about all these edge cases, because they’re pretty simple to use. Most developers only register one serviceWorker per page load, and they do not change the name of the serviceWorker file they deploy to the server.

The Workbox class includes this simpler view of the serviceWorker lifecycle by dividing all serviceWorker registrations into two categories: instance registered serviceWorker and external serviceWorker:

  • Registered serviceWorker: Since the Workbox instance’s call to Register () is just the serviceWorker to start installing, or if the call to Register () does not trigger the updatefound event during registration, the serviceWorker installation is enabled.
  • External serviceWorker: A serviceWorker that starts the register() installation independently of the Workbox instance. This usually happens when a user opens a new version of the site in another TAB.

The idea is that all life cycle events from the serviceWorker are events your code should expect, and all life cycle events from the external serviceWorker should be considered potentially dangerous and users should be warned accordingly.

With these two classes of Serviceworkers in mind, here’s a breakdown of all the important serviceWorker lifecycle moments, along with suggestions for developers on how to handle them:

Install serviceWorker for the first time

You may want to handle all future updates differently when serviceWorker is first installed.

In Workbox, you can distinguish between the first version installation and future updates by checking the isUpdate attribute for any of the following events. For the first installation, isUpdate will be false.

const wb = new Workbox('/sw.js');

wb.addEventListener('installed', (event) => {
  if(! event.isUpdate) {// Write the code needed for the first installation here}}); wb.register();Copy the code
moment The event Recommended operating
New serviceWorker installed (first time) installed When serviceWorker is first installed, it typically pre-caches all the resources a site needs to work offline. Consider notifying users that their site can now run offline.



In addition, since serviceWorker does not intercept the fetch event for the page load when it is first installed, you can also consider caching loaded resources (although this is not necessary if they are already pre-cached). The LIST of urls that send serviceWorker to the cache example above shows how to do this.
ServiceWorker already controls the page controlling After you install the new serviceWorker and start controlling the page, all subsequent fetch events will pass through the serviceWorker. If your serviceWorker adds any special logic to handle a particular FETCH event, this is the case when you know the logic will run.



Note that when serviceWorker is first installed, it does not start controlling the current page unless the serviceWorker calls clients.claim() in its Activate event. The default behavior is to wait until the next page loads to start control.



fromworkbox-windowFrom the point of view, this means only calls in serviceWorkerclients.claim()In the case of controlling events. If you already control the page before registration, this event will not be scheduled.
The serviceWorker has been activated activated As mentioned above, serviceWorker may (or may not) have started controlling the page the first time it completes activating it.



Therefore, you should not view the Activate event as a way to know when the serviceWorker controls the page. However, if you run logic in an active event (in the serviceWorker) and you need to know when the logic completes, the activated event will let you know.

When a newer version of serviceWorker is found

When a new serviceWorker is installed but an existing version is currently controlling the page, the isUpdate attribute for all of the following events will be true.

In this case, your reaction is often different from the first install, because you have to manage when and how users get this update.

moment The event Recommended operating
New serviceWorker installed (updated previous one) installed If this is not the first serviceWorker installation (event.isUpdate === true), indicating that a newer version of serviceWorker has been found and installed (that is, a different version than the current control page).



This usually means that a newer version of the site has been deployed to your server, and the new resources may have just been pre-cached.



Note: Some developers use installed events to notify users that a new version of their site is available. However, depending on whether I call skipWaiting() in the Install serviceWorker program, the installed serviceWorker may or may not take effect immediately. If you do call skipWaiting(), it’s better to notify the user of the update after the new serviceWorker is activated, or if you don’t call skipWaiting, it’s better to notify them to wait for pending updates in the event (see below for more information) details).
ServiceWorker is installed, but it is still in the waiting phase waiting If a newer version of serviceWorker is installed without calling skipWaiting(), it will not be activated until all pages controlled by the currently active serviceWorker have been uninstalled. You may want to notify the user that updates are available and will be applied the next time you visit.



Warning! Developers typically prompt users to reload to get updates, but in many cases refreshing the page does not activate the installed worker program. If the user refresh the page and serviceWorker still waiting, is waiting for the event will trigger again, and the event. The wasWaitingBeforeRegister attribute to true. Please note that we plan to improve this experience in future releases. Focus onQuestion # 1848To get updates.



Another option is to prompt the user and ask them if they want to get an update or continue to wait. If you choose to get updates, you can use postMessage() to tell the serviceWorker to run skipWaiting(). For an example, see Advanced recipes for page reloads for users.
ServiceWorker has started controlling the page controlling When the updated serviceWorker starts controlling the page, it means that the version of the serviceWorker currently under control is different from the version under control when the page was loaded. This may be fine in some cases, but it may also mean that some resources referenced by the current page are no longer in the cache (and possibly not on the server). You may need to consider notifying users that parts of the page may not work properly.



Note: If skipWaiting() is not called in the serviceWorker, no control event is emitted.
The serviceWorker has been activated activated When the updated serviceWorker is activated, it means that any logic you ran in the serviceWorker activation is complete. If something needs to be delayed until the logic is complete, this is the time to run it.

Find the unexpected version of serviceWorker

Sometimes users will open your site in background tabs for a long time. They may even open a new TAB and navigate to your site without realizing that they’ve already opened your site in the background TAB. In this case, your site may be running two versions at the same time, which can raise some interesting questions for developers.

Consider the case where tag A of your site is running V1 and tag B is running V2. When TAB B is loaded, it will be controlled by the serviceWorker version that comes with V1, but the page returned by the server (if using a network-first caching policy for navigating requests) will contain all v2 resources.

This is usually not a problem for TAB B, because when you write V2 code, you know how your V1 code works. However, it could be A problem with tag A, because your V1 code cannot predict what changes your V2 code might introduce.

To help handle these cases, Workbox-Window also schedules life cycle events when it detects updates from an “external” serviceWorker, where external represents any version that is not registered with the current WorkBox instance.

moment The event Recommended operating
The external serviceWorker has been installed externalinstalled If an external serviceWorker is installed, it may mean that the user is running a newer version of your site in a different TAB.



How you respond may depend on whether the installed service is in the wait or active phase.
Install the external serviceWorker by waiting for activation externalwaiting If the external serviceWorker is waiting to activate, it may mean that users are trying to get a new version of your site in another TAB, but they have been blocked because this TAB is still open.



If this happens, you might consider displaying a notification to the user asking them to close the TAB. In extreme cases, you might even consider calling window.reload() if doing so doesn’t cause the user to lose any saved state.
ServiceWorker The external serviceWorker is activated externalactivated If the external serviceWorker program is active, the current page may not continue to function properly. You may want to consider notifying users that they are running an older version of the page, and problems may arise.

Avoid Common Mistakes

One of the most useful features Workbox provides is its developer logging. The same is true for worbox-window.

We know that developing with serviceWorker can be confusing, and it’s hard to know why when things happen the opposite of what you expect.

For example, when you make a change to serviceWorker and reload the page, you may not see the change in your browser. The most likely reason is that your serviceWorker is still waiting to be activated.

But when you register a serviceWorker using the Workbox class, you’ll be notified of all lifecycle state changes in the developer console, which should help you debug why things don’t go as you expected.

In addition, a common mistake developers make when using serviceWorker for the first time is to register serviceWorker in the wrong scope.

To prevent this, the Workbox class warns you if the page that registered a service worker is not in the scope of that service worker. It also warns you if your service worker is active but not yet in control of the page:

Communication between Window and serviceWorker

Most advanced serviceWorker uses involve missing messaging between serviceWorker and Window. The Workbox class helps solve this problem by providing the messageSW() method, which registers the serviceWorker for the postMessage() instance and waits for a response.

While you can send data to serviceWorker in any format, the format shared by all Workbox packages is an object with three properties (the last two are optional) :

attribute Must be type describe
type is string A unique string that identifies this message.



By convention, types are uppercase and underscores separate words. If the type represents an action to be taken, it should be a command in the present tense (e.gCACHE_URLS), if the type represents reported information, it should be in the past tense (e.gURLS_CACHED).
meta no string In Workbox, this is always the name of the Workbox package that sent the message. When sending your own messages, you can omit this property or set it to whatever you like.
payload no * Data being sent. Usually this is an object, but it doesn’t have to be.

Messages sent through the messageSW() method use MessageChannel, so the recipient can respond to them. To respond to a message, you can call Event.ports [0].postMessage(response) in the message event listener. The messageSW() method returns a promise that will be resolved into any response you return.

This is an example of sending a message from the Window to the serviceWorker and getting a response. The first block is a message listener in serviceWorker, and the second block uses the Workbox class to send a message and wait for a response:

Code in sw.js:

const SW_VERSION = '1.0.0';

addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION); }});Copy the code

The code in main.js (running on Windows) :

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
Copy the code

Manage version incompatibility

The example above shows how to implement checking the serviceWorker version from a Window. Use this example because when you send messages back and forth between Windows and serviceWorker, it is important to note that your serviceWorker may not be running the same version of the site as your page code and that the solution to this problem may be different. It depends on whether you are network first or cache first.

The network is preferred

When serving your web pages first, your users will always get the latest version of HTML from your server. However, when users revisit your site for the first time (after deploying updates), they will get the latest version of HTML, but the serviceWorker running in their browser will be the previously installed version (possibly many older versions).

It’s important to understand this possibility, because if the JavaScript loaded by the current version of the page sends a message to an older version of the serviceWorker, that version may not know how to respond (or it may respond in an incompatible format).

Therefore, it is always a good idea to version control the serviceWorker and check for compatible versions before doing any critical work.

For example, in the code above, if the messageSW() call returns an earlier version of the serviceWorker than expected, it is better to wait until an update is found (this should happen when register() is called). At this point, you can notify users or update, or you can manually skip the wait phase to activate the new serviceWorker immediately.

The cache is preferred

First of all, when you first provide page caching, you know that your page will always initially be the same as the version of serviceWorker (because that’s why it was served). Therefore, it is safe to use messageSW() immediately.

However, if a newer version of serviceWorker is found and activated when the page calls Register () (that is, you intentionally skip the wait phase), it may no longer be safe to send a message to it.

One strategy to manage this possibility is to use a version control scheme that allows you to distinguish between interrupted updates and non-interrupted updates, and in the case of an update break, you know it is not safe to send a message to the serviceWorker. Instead, you need to warn users that they are running an older version of the page and suggest that they reload to get the update.


Blog name: Wang Leping blog

CSDN blogs at blog.csdn.net/lecepin


Creative Commons Attribution – Non-commercial Use – No Deductive 4.0 International License