When it comes to PWA, a lot of people are probably still at the level of “understanding”, not really trying it in practice, but playing with tutorials and examples on the Internet. However, the examples on the web are mostly simple demos and rarely integrated with real development, such as engineering with WebPack. This article will start with a Webpack plugin and talk about how to easily implement PWA using this webpack plugin called offline-Plugin.

Because there are so many articles about PWA, this article will not repeat the basic contents of “what is PWA” and “the life cycle of PWA”.

Offline-plugin plugin

  • offline-plugin
  • demo

First, automatic generationservice-worker.js

The core of PWA is service-worker (hereafter referred to as SW). Every PWA has only one service-worker.js file, which is used to add resource list, register, activate and other life cycle operations for SW. However, in a Webpack-built project, generating a service-worker.js can present two major problems:

  • 1. Most resources generated by Webpack will generate a string of hash, and these hash resources need to be updated synchronically in the resource list of SW;
  • 2. Every time the code is updated, the sw file version number needs to be updated to inform the client to update the cached resources. (The update can be triggered only if the sw code is different from the previous sw code, but a clear version number is more appropriate).

You might be wondering if the all-powerful WebPack community already provides a plugin to automate these things. The answer is yes. In addition to the officially recommended SW-Precach-webpack-plugin, there is the offline-plugin, which is the main character of today.

Compared with sw-Precach-webpack-plugin, OFFline-plugin has the following advantages:

  • 1, more optional configuration items to meet more detailed configuration requirements;
  • 2. More detailed documents and examples;
  • 3. Higher update frequency and more STARS;
  • 4, automatic processing of life cycle, users do not need to tangle with the life cycle pit;
  • *5, support AppCache;
  • 6. Automatic generationmanifestFile.
  • .

Two, basic use

The installation



npm install offline-plugin [--save-dev]Copy the code

Initialize the

First, go to webpack.config:



// webpack.config.js example

var OfflinePlugin = require('offline-plugin');

module.exports = {
  // ...

  plugins: [
    // ... other plugins
    // it's always better if OfflinePlugin is the last plugin added
    new OfflinePlugin()
  ]
  // ...
}Copy the code

Step 2: Add Runtime to your entry js file:



require('offline-plugin/runtime').install(a);Copy the code

ES6/Babel/TypeScript



import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();Copy the code

After the above steps, offline-plugin has been integrated into the project and can be built with WebPack.

Three, configuration,

As mentioned earlier, offline-plugin supports detailed configuration to meet different needs. The following describes some common configuration items for further use.

  • Caches: ‘all’ | Object



    Tells the plugin what to cache and how to cache 'all' : means that all resources built by Webpack, as well as those in the 'externals' option, will be cached. 'Object' : A configuration Object containing three arrays or re's (' main ', 'Additional', 'optional'), all of which are optional and empty by default. Default: 'all'.Copy the code
  • externals: Array<string>



    Allow developers to specify external resources (such as CDN references, or resources not generated through WebPack). With 'Caches' and' Additional ', you can cache external resources. Default: null Example: '['fonts/roboto.woff'] `Copy the code
  • ServiceWorker: Object | null | false



    This object contains multiple configuration items, but only the most common ones are listed here. 'events' : Boolean value. Allows the Runtime to accept messages from the SW. The default value isfalse. NavigateFallbackURL: When a URL request is not available from the cache or the network, it will be redirected to the URL that this option points to.Copy the code
  • AppCache: Object | null | false



    'offline-plugin' supports' AppCache 'by default, but the draft' AppCache 'has been deprecated by Web standards and is not recommended. But since there is still partial browser support, the plugin provides this functionality by default.Copy the code

Fourth, the runtime

The previous section introduced the configuration of offline-plugin in Webpack, and this section introduces some usage of the Runtime. For offline-plugin to take effect, the user must initialize the plugin via Runtime in the entry js file:



// Through AMD
require('offline-plugin/runtime').install();

// or use ES6/Babel/TypeScript

import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();Copy the code

The OfflinePluginRuntime object provides the following three methods:

  • Install (options: Object) Starts the ServiceWorker/AppCache installation process. This method is safe and must be called when the page is initialized. Also, do not place it in any conditional statements. (This sentence is not entirely true and will be explained in detail in the downgrading plan.)
  • ApplyUpdate () accepts updates from the currently installed SW.
  • Update () Checks the latest ServiceWorker/AppCache updates.

The Runtime.install () method takes a configuration object parameter that handles events in each lifecycle of the SW:

  • onInstalled



    When ServiceWorker/AppCache isinstall", can be used to show "APP already supports offline access".Copy the code
  • onUpdating



    AppCache does not support this method when the update information is fetched and the browser is updating the resource. At this moment, some resources are being downloaded.Copy the code
  • onUpdateReady



    Triggered when the 'onUpdating' event completes. At this point, all resources have been downloaded. The update is triggered by calling the runtime.applyUpdate() method.Copy the code
  • onUpdateFailed



    Triggered when the 'onUpdating' event fails for some reason. No resources are being downloaded, and all resource updates should be cancelled or skipped.Copy the code
  • onUpdated



    Triggered when an update is accepted.Copy the code

5. Downgrade plan

At some point we need to remove the SW and downgrade, we need to actively cancel the SW. However, offline-plugin does not provide the unregister() method for logging out of sw by default, so we need to implement it ourselves.

Really want to take the initiative to cancel sw is very simple, we can directly call ServiceWorkerContainer. GetRegistrations () method to get registration as an example, and then call registration. The unregister () method, The specific code is as follows:



if ('serviceWorker' in navigator) {
  navigator.serviceWorker.getRegistration().then((registration)= > {
    registration && registration.unregister().then((boolean)= > {
      boolean ? alert('Logout successful') : alert('Logout failed')}); })}Copy the code

After calling this method, the SW is logged out, and a refresh of the page shows that the resource is fetched from the network again.

In a real production environment, we could call the interface to decide whether to use the degradation scheme:



fetch(URL).then((switch)= > {
  if (switch) {
    OfflinePluginRuntime.install()
  } else {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.getRegistration().then((registration)= > {
        registration && registration.unregister().then((boolean)= > {
          boolean ? alert('Logout successful') : alert('Logout failed')})})}})Copy the code

Six, encountered pit

In practice, a relatively big hole encountered, is the update of sw.js file.

In the design of the service worker, the browser requests sw.js again every time it loads the URL of the site. If the sw.js content is found to be different from the previous one, it will be judged as a resource update and trigger the sw life cycle again. However, sw.js is also a normal JS resource file that defaults to the expired time set by the server, which is its max-age. After understanding the design of the service worker, it is not hard to see that the max-age of sw.js should be as short as possible so that the browser can update the resource list in a timely manner.

This is the same problem I discovered when I used HTTP-Server directly during the research phase. Later, in the official example, I found the NPM script to say:



"start":"http-server ./dist -p 7474 -c no-cache"Copy the code

It is worth noting that all resources are explicitly not cached.

In addition, offline-plugin cannot be used properly in webpack-dev-server because it requires a specific file to generate sw.js, whereas for projects built with webpack-dev-server, the files are stored in memory. Therefore, it cannot be used with offline-plugin normally. You are advised to use offline-plugin only in production mode.

Seven, the end

One of the great features of PWA is that it can be added to the home screen, but this has not been implemented due to the lack of a suitable Android phone. If any readers have done this, please join me in the discussion.