The background,

A review of the top Events and trends in 2017: Where to go in 2018? The PWA is one of the top 10 things to watch for in the front end of 2017.

We all know that Native apps have a really good experience and are easy to access once downloaded to your phone. It also has some disadvantages:

  • High development costs (ios and Android)
  • Software online needs to be reviewed
  • Version updates require uploading the new version to a different app store
  • If you want to use an app, you have to download it to use it, even if you need to use it occasionally

And the web page development cost is low, the website update upload the latest resources to the server can be opened with a mobile phone browser can be used. However, the experience is still worse than Native app, and there are some obvious disadvantages

  • Mobile desktop entry is not convenient enough. To enter a page, you have to memorize its URL or bookmark it
  • No network, no response, no offline capability
  • Unlike APP, it can not push messages

So what is PWA?

Second, What ‘s the PWA?

PWA stands for Progressive Web App, or Progressive Web application.

A PWA application is first and foremost a Web page, and a Web application can be written using Web technology. What problems have been solved by adding App Manifest and Service Worker to realize PWA installation and offline functions?

  • Can be added to the main screen, click the main screen icon can realize the start animation and hide the address bar
  • Realize the offline cache function, even if the user’s mobile phone does not have the network, still can use some offline functions
  • Message push is implemented

It solves the problems mentioned above, and these features will make Web applications progressively closer to native apps.

Realization of PWA

3.1 Manifest implementation added to the home screen

index.html

<head>
  <title>Minimal PWA</title>
  <meta name="viewport" content="width=device-width, user-scalable=no" />
  <link rel="manifest" href="manifest.json" />
  <link rel="stylesheet" type="text/css" href="main.css">
  <link rel="icon" href="/e.png" type="image/png" />
</head>
Copy the code

manifest.json

{
  "name": "Minimal PWA"// Specify the name of the plug-in to display"short_name": "PWA Demo", // Optionally displayed in APP Launcher and the new TAB page, if not set, use name"description": "The app that helps you understand PWA"// Describes the application"display": "standalone", // Define the developer's preferred display mode for Web applications. Standalone mode will have a separate"start_url": "/", // The application startup URL"theme_color": "# 313131"// The background color of the desktop icon"background_color": "# 313131", // A predefined background color for the Web application. A smooth transition is created between starting the Web application and loading the content of the application."icons"[// desktop icon, which is an array {"src": "icon/lowres.webp"."sizes": "48x48", // Image sizes separated by Spaces"type": "image/webp"// Help userAgent quickly rule out unsupported types}, {"src": "icon/lowres"."sizes": "48x48"
  },
  {
    "src": "icon/hd_hi.ico"."sizes": "72x72 96x96 128x128 256x256"
  },
  {
    "src": "icon/hd_hi.svg"."sizes": "72x72"}}]Copy the code

The Manifest reference: developer.mozilla.org/zh-CN/docs/…

Can open the web site developers. Google. Cn/web/showcas… View the giFs added to the home screen.

If you’re using an Android phone, download the Chrome browser and see for yourself

3.2 Service worker offline cache

3.2.1 What is a service worker

Service Worker is a WEB API proposed and promoted by the Chrome team to provide advanced and sustainable background processing power for WEB applications.

Service Workers are like an interceptor between a server and a web page, intercepting HTTP requests as they come in and out, giving you complete control over your site.

The most important feature

  • After the page is registered and installed successfully, it runs in the background of the browser and is not affected by page refresh. It can monitor and block HTTP requests of all pages within the scope.
  • Websites must use HTTPS. Except when debugging with the local development environment (such as using localhost for domain names)
  • Running in the browser background, you can control all page requests under the open scope
  • Separate scope, separate runtime environment and execution thread
  • Page DOM cannot be manipulated. But it can be handled through the event mechanism
  • Event-driven service threads

If a service worker is installed on an HTTP site, it will be vulnerable to attack

Browser Support

For details about browser support, see caniuse.com/#feat=servi…

The life cycle

When the user navigates to the URL for the first time, the server returns the responding web page.

  • Step 1: When you call the register() function, the Service Worker starts downloading.
  • Step 2: During registration, the browser downloads, parses, and executes the Service Worker (). If anything goes wrong in this step, the promise returned by register() executes reject, and the Service Worker is deprecated.
  • Step 3: Once the Service Worker has successfully executed, the Install event is activated
  • Step 4: When the installation is complete, the Service Worker is activated and controls everything within its scope. If all events in the life cycle succeed, the Service Worker is ready and ready to use!

Chrome ://serviceworker-internals to check the details of all Service workers installed in the current browser

3.2.2 HTTP cache and Service worker Cache

  • HTTP cache

A Web server can use the Expires header to inform a Web client that it can use the current copy of a resource until a specified “expiration time.” In turn, the browser can cache this resource and only check the new version again after the expiration date. Using HTTP caching means that you rely on the server to tell you when to cache resources and when to expire.

  • The service worker cache

The power of Service Workers lies in their ability to intercept HTTP requests into any incoming HTTP request and decide how they want to respond. In your Service Worker, you can write logic to decide what resources you want to cache, what conditions you need to meet, and how long the resources need to be cached. You’re in control!

3.2.3 Implementing offline cache

index.html

<! DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello Caching World! </title> </head> <body> <! -- Image --> <img src="/images/hello.png"/ > <! -- JavaScript --> <script async src="/js/script.js"></script> <script> // Register service workerif ('serviceWorker' in navigator) {           
        navigator.serviceWorker.register('/service-worker.js', {scope: '/'}).then(functionConsole. log('ServiceWorker registration successful with scope: ', registration.scope);
        }).catch(function(err) {// failed to register :(console.log('ServiceWorker registration failed: ', err);
        });
      }
    </script>
  </body>
</html>
Copy the code

Note: The registered path of a Service Worker determines the scope of the default page used by its scope. If service-worker.js is in the /sw/ page path, this causes the service worker to receive only fetch events in the /sw/ page path by default. If stored at the root of a website, all fetch events for that website will be received. If you want to change its scope, set the scope in the second argument. The example changes it to the root directory, which is valid for the entire site.

service-worker.js

var cacheName = 'helloWorld'; // Cache name // Install event, which occurs when the browser installs and registers the Service Worker self.addEventListener('install', event => {/* event.waitUtil is used to perform some pre-installed logic until the installation is successful, but it is recommended to cache only lightweight and very important resources. After the installation is successful, the ServiceWorker status changes from Installing to Installed */ Event.waituntil (caches. Open (cacheName). Then (cache => Cache.addall ([// If all files are successfully cached, the installation is complete. If any files fail to download, the installation process fails.'/js/script.js'.'/images/hello.png'))); }); /** Add an event listener for the FETCH event. Next, the caches.match() function is used to check that the incoming request URL matches anything currently in the cache. Returns the cached resource if it exists. If the resource does not exist in the cache, obtain the resource over the network and add the obtained resource to the cache. */ self.addEventListener('fetch'.function (event) {
  event.respondWith(
    caches.match(event.request)                  
    .then(function (response) {
      if (response) {                            
        return response;                         
      }
      var requestToCache = event.request.clone();  //          
      return fetch(requestToCache).then(                   
        function (response) {
          if(! response || response.status ! = = 200) {return response;
          }
          var responseToCache = response.clone();          
          caches.open(cacheName)                           
            .then(function (cache) {
              cache.put(requestToCache, responseToCache);  
            });
          returnresponse; })); });Copy the code

Note: The reason for using request.clone() and Response.clone () is because request and response are a stream that can only be consumed once. Because we consumed the request once through the cache and consumed it again when making the HTTP request, we need to Clone the request — a request is a stream and can only be consumed once.

3.2.4 Debugging related

Open chrome googlechrome. Making. IO/samples/ser… , this is a website that implements the offline caching function of service worker, open the debugging tool

Introduce 1. And 2 in a diagram.

  1. Check it to simulate the offline situation of the website. After check it, the network will have a yellow warning icon, indicating that the website is offline. After you refresh the page, the page is still displayed normally
  2. Scope of the current service worker. It is able to intercept requests under https://googlechrome.github.io/samples/service-worker/basic/index.html, Also to intercept https://googlechrome.github.io/samples/service-worker/basic/ * / *. HTML under request

The debug panel concrete representative of what see x5.tencent.com/tbs/guide/s… The third part

3.3 Serice Worker implements message push

  • Step 1 prompt the user and get their subscription details
  • Step 2. Save these details on the server
  • Step 3. Send any messages as needed

Different browsers need different push message servers. For example, using Google Cloud Messaging as a push service on Chrome, the first step is to register the applicationServerKey(obtained via GCM registration) and subscribe or initiate a subscription on the page. Each session will have an independent endpoint (the endpoint), subscribe to the object’s properties (PushSubscription. The endpoint) is the endpoint value. After sending the endpoint to the server, the server uses this value to send a message to the active Service Worker of the session (communicating with the browser client via GCM).

Step 1 and step 2 index.html

<! DOCTYPE html> <html> <head> <meta charset="UTF-8">
    <title>Progressive Times</title>
    <link rel="manifest" href="/manifest.json">                                      
  </head>
  <body>
    <script>
      var endpoint;
      var key;
      var authSecret;
      var vapidPublicKey = 'BAyb_WgaR0L0pODaR7wWkxJi__tWbM1MPBymyRDFEGjtDCWeRYS9EF7yGoCHLdHJi6hikYdg4MuYaK0XoD0qnoY'; // The method is very complex, but can not look at the specific knowledge used to transform vapidPublicKeyfunction urlBase64ToUint8Array(base64String) {                                  
        const padding = '='.repeat((4 - base64String.length % 4) % 4);
        const base64 = (base64String + padding)
          .replace(/\-/g, '+')
          .replace(/_/g, '/');
        const rawData = window.atob(base64);
        const outputArray = new Uint8Array(rawData.length);
        for (let i = 0; i < rawData.length; ++i) {
          outputArray[i] = rawData.charCodeAt(i);
        }
        return outputArray;
      }
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('sw.js').then(function (registration) {
          return registration.pushManager.getSubscription()                            
            .then(function (subscription) {
              if (subscription) {                                                      
                return;
              }
              return registration.pushManager.subscribe({                              
                  userVisibleOnly: true,
                  applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)
                })
                .then(function (subscription) {
                  var rawKey = subscription.getKey ? subscription.getKey('p256dh') : ' ';
                  key = rawKey ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : ' ';
                  var rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : ' ';
                  authSecret = rawAuthSecret ?
                    btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : ' ';
                  endpoint = subscription.endpoint;
                  return fetch('./register', {                                         
                    method: 'post',
                    headers: new Headers({
                      'content-type': 'application/json'
                    }),
                    body: JSON.stringify({
                      endpoint: subscription.endpoint,
                      key: key,
                      authSecret: authSecret,
                    }),
                  });
                });
            });
        }).catch(function(err) {// failed to register :(console.log('ServiceWorker registration failed: ', err);
        });
      }
    </script>
  </body>
</html>
Copy the code

Step 3 The server sends a message to the service worker

app.js

const webpush = require('web-push');                 
const express = require('express');
var bodyParser = require('body-parser');
const app = express();
webpush.setVapidDetails(                             
  'mailto:[email protected]'.'BAyb_WgaR0L0pODaR7wWkxJi__tWbM1MPBymyRDFEGjtDCWeRYS9EF7yGoCHLdHJi6hikYdg4MuYaK0XoD0qnoY'.'p6YVD7t8HkABoez1CvVJ5bl7BnEdKUu5bSyVjyxMBh0'
);
app.post('/register'.function (req, res) {           
  var endpoint = req.body.endpoint;
  saveRegistrationDetails(endpoint, key, authSecret); 
  const pushSubscription = {                          
    endpoint: req.body.endpoint,
    keys: {
      auth: req.body.authSecret,
      p256dh: req.body.key
    }
  };
  var body = 'Thank you for registering';
  var iconUrl = 'https://example.com/images/homescreen.png'; SendNotification (pushSubscription, json.stringify ({MSG: body, url:'http://localhost:3111/',
        icon: iconUrl
      }))
    .then(result => res.sendStatus(201))
    .catch(err => {
      console.log(err);
    });
});
app.listen(3111, function () {
  console.log('Web push app listening on port 3111! ')});Copy the code

The service worker listens for push events and pushes notification details to the user

service-worker.js

self.addEventListener('push'.function(event) {// Check whether the server sent any payload data. JSON.parse(event.data.text()) :'no payload';
  var title = 'Progressive Times'; Event. WaitUntil (/ / using the provided information to display Web push notification. Self registration. ShowNotification (title, {body: payload. MSG, url: payload.url, icon: payload.icon }) ); });Copy the code

[service worker update](https://lzw.me/a/pwa-service-worker.html#3.3 service worker update)

conclusion

The advantage of the PWA

  • App shortcuts can be placed on the desktop and run in full screen, just like a native app
  • It can be used in various network environments, including poor network and disconnection conditions, without displaying undeFind
  • The ability to push messages
  • Its essence is a web page, without the various startup conditions of a native app, quickly respond to user instructions

Problems with PWA

  • Low support: PWA is not supported on ios and Internet Explorer
  • Chrome’s share of the Chinese desktop is good, but android mobile is low
  • Major vendors have yet to explicitly support PWA
  • The dependent GCM service is unavailable in the country
  • Wechat mini program competition

Despite some of the drawbacks mentioned above, PWA technology still has many uses.

  • Service worker technology realizes offline cache, which can put some static files that are not frequently changed into the cache to improve user experience.
  • Service worker realizes message push and uses browser push function to attract users
  • Progressive development, although not currently supported by some browsers, can leverage the techniques described above to bring a better experience to users of supported browsers.

Reference documentation

  • The first PWA Chinese book
  • PWA English books
  • Incremental Enhanced Experience (PWA) transformation: Service Worker application details
  • Basic Service Worker Sample
  • 【翻译】Service Worker entry
  • Web App Manifest
  • Service Workers: an Introduction
  • The Offline Cookbook
  • Comparative analysis of wechat applets and PWA
  • Service Worker best practices