Use offline-plugin with webpack to easily implement PWA

  Front end, frontend, javascript, pwa, webpack

clipboard.png

When talking about PWA, many people may still only stay at the level of “understanding” and have not really tried it in practice. More often, they have only probably tried online tutorials and examples. However, the examples on the network are mostly simple demo, rarely combined with real development, such as the engineering combination with webpack. This article will start with a webpack plugin and talk about how to use this web pack plugin calledoffline-pluginPWA is easily implemented by the webpack plug-in of.

Since there are too many articles related to PWA, this article will not repeat the basic contents such as “what is PWA” and “PWA’s life cycle”.

offline-pluginRelated links:

I. Automatic Generationservice-worker.js

The core of PWA isservice-worker(hereinafter referred to as SW), any PWA has and only has oneservice-worker.jsThe file is used to add a resource list for SW and perform life cycle operations such as registration and activation. However, in a project built with a webpack, aservice-worker.jsThere may be two big problems:

  • 1. Most of the resources generated by the webpack will generate a string of hash. sw’s resource list needs to update these resources with hash synchronously.
  • 2. Every time the code is updated, the client needs to be notified to update the cached resources by updating the sw file version number. (In fact, as long as the sw code this time is different from the previous sw code, the update can be triggered, but it is more appropriate to use a clear version number).

Seeing this, you may have thought, has the universal webpack community provided the corresponding plugin to help us handle these things automatically? The answer is yes. In addition to the official recommendationsw-precache-webpack-pluginIn addition, there are our main characters today.offline-plugin.

Compared withsw-precache-webpack-pluginIn my opinionoffline-pluginHas the following advantages:

  • 1. More optional configuration items to meet more detailed configuration requirements;
  • 2. More detailed documents and examples;
  • 3. The update frequency is relatively higher and the number of star is more.
  • 4, automatically processing the life cycle, and users do not need to struggle with the pit of the life cycle;
  • *5. AppCache; is supported;
  • 6. Automatic GenerationmanifestDocuments.

II. Basic Use

Installation

npm install offline-plugin [--save-dev]

Initialization

Step one, enterwebpack.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()
 ]
 //   ...
 }

The second step, theruntimeAdd to your portal js file:

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

ES6/Babel/TypeScript

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

After the above steps,offline-pluginIt has been integrated into the project and can be built through a webpack.

III. Configuration

As I said earlier,offline-pluginSupport detailed configuration to meet different needs. The following will introduce several commonly used configuration items for your further use.

  • Caches: ‘all’ | Object

    Tell the plug-in what to cache and how to cache it.
     `all': means that all resources built by the webpack, as well as those in the externals' option, will be cached.
     `Object`: Contains three arrays or regular configuration Object (main`, `additional`, `optional), which are optional and are empty by default.
     Default: `all'.
  • externals: Array<string>

    Allows developers to specify some external resources (such as CDN references or resources not generated through webpack)  .  The function of caching external resources can be realized by matching the' additional' item of the' Caches'.
     
     Default: null'
     For example: ` ['fonts/roboto.woff'] `
  • ServiceWorker: Object | null | false

    This object contains multiple configuration items, only the most commonly used are listed here.
     
     Events': boolean value.  Allows the runtime to accept messages from sw with a default of false.
     NavigateFallbackURL': When a URL request cannot be obtained from the cache or the network, it will be redirected to the URL pointed to by this option.
  • AppCache: Object | null | false

    "offline-plugin" supports "AppCache" by default, but the "AppCache" draft has been abandoned by web standards and is not recommended.
     However, because some browsers still support it, the plug-in provides this function by default.

IV. runtime

The previous section describedoffline-pluginFor the configuration in the webpack, this section will introduce some usage of the runtime.
To makeoffline-pluginTo take effect, the user must initialize in the entry js file through the runtime:

//via AMD
 require('offline-plugin/runtime').install();
 
 //or through ES6/Babel/TypeScript
 
 import * as OfflinePluginRuntime from 'offline-plugin/runtime';
 OfflinePluginRuntime.install();

OfflinePluginRuntimeObject provides the following three methods:

  • install(options: Object)
    Open the installation process of ServiceWorker/AppCache. This method is secure and must be called when the page is initialized. In addition, please do not put it in any conditional statement. (This sentence is not entirely correct, and will be described in detail in the following downgrade plan.)
  • applyUpdate()
    Accept the update information of sw currently installed.
  • update()
    Check the update information of the new version of ServiceWorker/AppCache.

runtime.install()The method accepts a configuration object parameter that is used to process events in each life cycle of sw:

  • onInstalled

    When ServiceWorker/AppCache is install, it can be used to show that "APP already supports offline access."
  • onUpdating

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

    Triggered when the' onUpdating' event completes.  At this time, all resources have been downloaded.
     Runtime.applyUpdate ()' method to trigger the update.
  • onUpdateFailed

    Triggered when the' onUpdating' event fails for some reason.
     At this time, no resources are downloaded and all resource update processes should be canceled or skipped.
  • onUpdated

    Triggered when the update is accepted.

V. downgrade plan

When we need to remove sw for demotion at certain times, we need to actively cancel sw. However,offline-pluginBy default, there is no provision for logging off sw.unregister()Method, so we need to implement it ourselves.

In fact, it is very simple to log off sw voluntarily, we can call it directlyServiceWorkerContainer.getRegistrations()How to get itregistrationInstance, and then call theregistration.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')
 });
 })
 }

After calling this method, sw has been logged off, refresh the page to see that the resources are retrieved from the network again.

In a real production environment, we can decide whether to use a downgrade scheme by calling the interface:

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')
 })
 })
 }
 }
 })

VI. Pits Encountered

In the specific practice, encountered a relatively large pit, issw.jsUpdate of files.

In the design of service worker, every time the browser loads the URL of the site, it will request it again.sw.js. If found this timesw.jsIf the content is different from the previous one, it will be judged as resource update and the sw life cycle will be re-triggered. However,sw.jsIt is also a common js resource file, which will use the expired time set by the server by default, that is, itsmax-age. After understanding the design of service worker, we can easily find that,sw.jsThemax-ageIt should be as short as possible so that the browser can update the resource list in time.

This is also my direct use in the research phase.http-serverIn the past few days. Later in the official example, I found thatnpm scriptIt reads as follows:

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

It is worth our attention to directly specify that all resources do not use cache.

In addition,webpack-dev-serverIt cannot be used normally.offline-pluginBecause it requires specific files to generatesw.jsBut throughwebpack-dev-serverThe files of the built project are stored in memory, so they cannot be compared withoffline-pluginIt is normally used together. It is recommended thatProduction modeInternal useoffline-plugin.

Seven, add to the main screen

Mobile browsers all provide the function of “adding to the home screen”, but ordinary websites add to the home screen only by putting the bookmarks of the websites on the desktop. If you want to add the website to the main screen as PWA, we need oneJson file

{
 "name": "offline-plugin",
 "icons": [
 {
 "src": "/android-chrome-192x192.png",
 "sizes": "192x192",
 "type": "image/png"
 },
 {
 "src": "/android-chrome-512x512.png",
 "sizes": "512x512",
 "type": "image/png"
 }
 ],
 "theme_color": "#181743",
 "background_color": "#181743",
 "start_url": "/",
 "display": "standalone"
 }

Then, take thismanifest.jsonAnd otherStatic resourcesPack them in the root directory of the website:

clipboard.png

Sample address:

clipboard.png

Open chrome Developer Tool and enterApplicationOne column, selectManifest, you can see the effect:

clipboard.png

ByCurrently (15 August 2017), what I useiOS10.3.2The version of iPhone7 already supports PWA, with the following effects:
After consulting a large amount of data, so far, iOS does not support PWA, but it can add several tags in html to achieve similar experience effects of web pages and native APP:

Apply icon:
 <link rel="apple-touch-icon" href=“/custom_icon.png">
 
 Startup screen:
 <link rel="apple-touch-startup-image" href="/launch.png">
 
 Application name:
 <meta name="apple-mobile-web-app-title" content="AppTitle">
 
 Full screen effect:
 <meta name="apple-mobile-web-app-capable" content="yes">
 
 Set the status bar color:
 <meta name="apple-mobile-web-app-status-bar-style" content="black">

Open with safari
clipboard.png

Open after adding to main screen
clipboard.png

Open from home screen after offline

clipboard.png

Open task manager

clipboard.png

It can be seen that PWA exists as an independent APP in terms of performance and function.

Viii. conclusion

Originally, I always thought Apple did not support PWA well, but through this practice, we can know that PWA has made great progress and developers can build their own PWA happily.
The conclusion cannot be drawn too early. . .