Progressive Web App, PWA for short, is a new way to improve the experience of Web App. It can give users the experience of native application. It is dedicated to developing a series of solutions using cutting-edge technology to make the Web experience like native App.

Progressive Web Apps with answers from Google Developers:

  • Progressive – suitable for all modern browsers as it was developed with progressive enhancement in mind
  • offline– with the help ofService WorkerIt can be accessed normally when offline or the network is poor
  • Installable – Users can add shortcuts to the desktop and generate one-click access
  • Similar applications– Because it’s inApp ShellModel based development as should beNative AppInteraction and navigation to the userNative AppThe experience of
  • Continuous update – always up to date, no version and update issues
  • security– byHTTPSAgreements provide services to prevent snooping and ensure that content is not tampered with
  • But the index– Apply manifest files andService WorkerCan be indexed by search engines to identify it as an “application.”
  • Stickiness – if the page has been closed, you can also push background notifications to let users return

We can also see the actual effect with a DEMO => weather PWA

Among them, the most important core functions of THE PWA solution are implemented by relying on the Service Worker API.


What is a Service Worker?

As early as May 2014, W3C proposed a Service Worker AS an HTML5 API, which is mainly used for persistent offline caching.

Of course, the API didn’t come out of nowhere, but here’s a simple explanation:

JavaScript in browsers runs on a single main thread and can only do one thing at a time. With the increasing complexity of Web business, we gradually add a lot of complex computing processes that consume resources and time into JS, and the performance problems caused by these processes become more prominent in the complex process of WebApp.

The W3C had an early insight into the possible impact of these problems. At this time, an API called Web Worker was created. The sole purpose of this API was to free the main thread. After completion, the main thread is told through the postMessage method, and the main thread gets the result feedback from the Web Worker through the onMessage method.

Everything seems to be fixed, but since the Web Worker is temporary (i.e. the browser closes), can we have something that is always persistent and ready to take orders from the main thread? Based on this requirement, the initial version of the Service Worker was launched, which added persistent offline caching capabilities to the Web Worker.

Service Workers have the following functions and features:

  • A separate worker thread, independent of the current page process.
  • Once installed, it exists forever unless it is uninstall
  • You can wake up when you need to and automatically sleep when you don’t
  • Programmable interception of proxy requests and returns, cache files, cached files can be accessed by web processes (including network offline state)
  • Offline content developers can control
  • Can push messages to the client
  • You cannot manipulate the DOM directly
  • For security reasons, it must work in an HTTPS environment

So we basically know the great mission of the Service Worker, which is to make the cache elegant and extreme, make the shortcomings of Web App more weakened compared with Native App, and also provide developers with infinite imagination on performance and experience.

How the Service Worker works

The technical core of the Service Worker is the Service Worker script, which is a browser-side agent script written in Javascript.

When the front-end page initiates registration to the kernel, the script address will be notified to the kernel, and the kernel will start the standalone/thread to load the Service Worker script and perform the Service Worker installation and activation actions. After successful activation, it enters the idle waiting state. If the current Service Worker enters the page or event message that has not been managed by the thread, it will automatically terminate (the specific termination policy depends on different browsers and versions, and will not affect the front-end writing logic. But the front end does not save the information that needs to be persisted in the Service Worker script, can use localstorage), when opening a new can be managed page or has been managed page initiated message and other messages, the Service Worker into/thread will be re-aroused.

Whenever the installed Service Worker has a jurisdiction page open, the Service Worker script is triggered to update, and when the Service Worker script changes, the Service Worker script from the local network cache is pulled directly from the network. If there is a one-byte difference between the network pull and the local pull, the update of the Service Worker script will be triggered. The update process is similar to the installation process, except that the Service Worker of the old version will not enter the active state immediately after the update is successfully installed, and the Service Worker of the old version needs to wait for the entry/thread termination.

The sample code

// Register the service worker in HTML
if(navigator.serviceWorker ! =null) {
    navigator.serviceWorker.register('sw.js')
      .then(function(registration) {
        console.log('Registered events at scope: ', registration.scope);
      });
  }	
Copy the code
// First define the path to cache and the list of static files to cache.
var cacheStorageKey = 'minimal-pwa-8'

var cacheList = [
  '/'."index.html"."main.css"."e.png"."*.png"
]

// With the Service Worker, you can grab resources and write them to the cache when the Service Worker is installed:
window.addEventListener('install'.function(e) {
  console.log('Cache event! ')
  e.waitUntil(
    caches.open(cacheStorageKey).then(function(cache) {
      console.log('Adding to Cache:', cacheList)
      return cache.addAll(cacheList) 
    }).then(function() {
      console.log('Skip waiting! ')
      return self.skipWaiting()
    })
  )
})

// During the process of fetching resources, the Service Worker can capture the fetch event and write code to determine how to respond to the resource request:
window.addEventListener('fetch'.function(e) {
  // console.log('Fetch event:', e.request.url)
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if(response ! =null) {
        console.log('Using cache for:', e.request.url)
        return response
      }
      console.log('Fallback to fetch:', e.request.url)
      return fetch(e.request.url)
    })
  )
})
Copy the code

About the event

Install event: When the current Service Worker script is installed, the install event is triggered.

Push events: Push events are reserved for push notifications. But first you need to understand the Notification API and the PUSH API.

Through the PUSH API, after subscribing to the PUSH Service, the Service Worker can be awakened in PUSH mode to respond to the message from the system messaging Service, even if the user has closed the page.

Online/Offline event: When the network status changes, the online or offline event is triggered. These two events can be combined with the Service Worker to achieve a better offline experience, such as replacing/hiding link navigation that requires online status when the network changes.

Fetch event: When the Service Worker is successfully installed and activated, it runs in the background of the browser. Our thread will always monitor our page application, and if an HTTP request occurs, it will trigger the FETCH event and give its own response. This capability is so powerful that the Fetch API and Cache API can be used to write complex policies to distinguish between different types or pages of resource handling. It can provide a better user experience: for example, caching first, degrading policy logic can be implemented: monitoring all HTTP requests and returning the cached content when the requested resource is already in the cache; Otherwise, use the FETCH API to continue the request. If it is an image or CSS or JS resource, add it to the cache after the request succeeds. If the status is offline or the request is faulty, the pre-cached offline content is degraded and returned.


Use the WebPack plug-in

A lot of people are wondering here, since resources can be cached through the service-worker, how does the front end know and cache new resources in real time after a project iteration and after the code is pushed to the formal environment?

The first is to manually change the version number of the SW file for each change, triggering the update.

The second is to automate the process using webPack plug-ins

In fact, in our real webpack-generated project, there are two problems with manually writing the service-worker. js file in the first way:

  1. webpackThe generated resource will generate a string of hashes,Service-worker.jsWe need to update these hashed resources synchronously in the resource list.
  2. Every time the code is updated, you need to pass the updateservice-workerFile version number to notify the client to update the cached resource.

The sw-precache-webpack-plugin is also recommended. The offline plugin not only solves the cache update problem mentioned earlier, but also has the following advantages:

  • 1, automatically generate and update the service-worker. js file and automatically add cache resource list for SW
  • 2. More detailed documentation and examples;
  • 3. The iteration frequency is relatively higher and the number of stars is more;
  • 4, automatic processing of life cycle, users do not need to tangle life cycle pit;
  • 5, support automatic generationmanifestFile.

It’s also pretty simple to deploy to a project

1. Install

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

2. The initialization

First step, go to webpack.config.js:

// 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

3. Import the import file

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

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

You can also view the specific code in demo


Browser support for PWA

Compatibility check

After all,chrome is certainly the most supported browser, and chrome64 basically supports all the PWA API features.

However, the support of domestic browsers is relatively poor, and chrome mobile version is still a small number of users, but the support of UC is not low