Preface:

Everyone has heard of PWA and it will be used in the actual project development. One of its most important functions is to realize the offline cache of the client, followed by the message push of the server. Some of our existing Webapps have poor user experience (unable to access offline) and low user stickiness (unable to save entry). Pwa aims to solve these problems (Progressive Web Apps) and make WebApps fast, reliable and safe.

PWA series of technologies used

  • Web App Manifest (Generate desktop ICONS)
  • Service Worker (offline access)
  • Push Api & Notification Api (Server side message Push)
  • App Shell & App Skeleton

A. A Web App Manifest

Add the site to the desktop for a more native like experience.

For Android phones:

<link rel="apple-touch-icon" href="/icon.png" /> {"name": "PWA effect display ", // appname "short_name": Standalone, // fullScreen (standalone) minimal- UI browser ✓ "start_URL ": standalone, // fullScreen (standalone) minimal- UI browser ✓" start_URL ": "/", / / open the url ✓ "ICONS" : [{/ / set the desktop picture icon icon "SRC" : "/ icon. PNG", "sizes" : "144 x144", "type" : "Image/PNG"}], "background_color": "#aaa", //Copy the code

Ios does not support the manifest file, which can be set using the meta/link private attribute

<! - icon icon - > < link rel = "apple - touch - icon" href = "/ icon. PNG" / > <! <meta name="apple-mobile-web-app-title" content=" title" > <! Hide safari address bar standalone mode hide by default --> <meta name="apple-mobile-web-app-capable" content="yes" /> <! <meta name="apple-mobile-web-app-status-bar-style" content=" black-always "/>Copy the code

2. The Service Worker

Service Worker features:

  • Dom cannot be accessed/manipulated
  • Will automatically sleep, will not be disabled when the browser closed (must be manually uninstalled)
  • Offline cache content developers can control
  • This parameter must be used in HTTPS or localhost
  • All apis are based on Promise

Life cycle:

  • Installation: This state occurs after the Service Worker registers and the installation begins, triggering the install event callback to specify some static resources for offline caching.
  • After installed: The Service Worker has completed the installation and is waiting for other Service Worker threads to be closed.
  • Activating: The client that is not controlled by other Service workers in this state allows the current Worker to complete the installation, clears other workers and the old cache resources associated with the cache, and waits for the new Service Worker thread to be activated.
  • Activated: The activate event callback is processed in this state (providing an opportunity to update the cache policy). It can also handle functional fetch, sync, and push events.
  • Redundant state: This state indicates the end of a Service Worker’s life cycle.

The key way to

  • Self.skipwaiting (): forces the Service Worker in waiting to enter the activate state
  • Event.waituntil () : Pass a Promise until the Promise is in the resolve state.
  • Self.clients.claim () : Execute this method in the Activate event callback to gain control of the page so that the version update cache is used when the page is opened later. The old Service Worker script no longer controls the page and will be stopped later.

Key steps:

1. Register with serviceWorker

window.addEventListener('load', async () => { if ('serviceWorker' in navigator) { let registration = await navigator.serviceWorker.register('/sw.js'); }});Copy the code

ServiceWorker is re-registered whenever the content in sw.js changes

2. Register the listener function

Create a sw.js file

self.addEventListener('fetch',(e)=>{ console.log(e.request.url); // intercepting requests}); Self.addeventlistener ('install',(e)=>{only the first install will execute this callback e.waituntil (skipWaiting()); }); Self.addeventlistener ('activate',(e)=>{this callback is executed only after the first activation e.waituntil (self.clients.claim()); // Give serviceWorker control}) self points to the current browser processCopy the code

The above three steps are key

3. Cache static resources

The cache list is cached during installation

const CACHE_NAME = 'cache_v' + 1; const CACHE_LIST = [ '/', '/index.css', '/main.js', '/api/list', '/index.html', ]; async function preCache(){ let cache = await caches.open(CACHE_NAME); await cache.addAll(CACHE_LIST); await self.skipWaiting(); } self.addEventListener('install',(e)=>{ e.waitUntil(preCache()); });Copy the code

Delete useless caches after activation

async function clearCache(){ self.clients.claim(); let keys = await caches.keys(); await Promise.all(keys.map(key=>{ if(key ! == CACHE_NAME){ return caches.delete(key); }})); } self.addEventListener('activate',(e)=>{ e.waitUntil(clearCache()); // Give serviceWorker control});Copy the code

4. Use caching offline

self.addEventListener('fetch',(e)=>{ let url = new URL(e.request.url); if(url.origin ! == self.origin){// Static resources go to browser cache return; } e.respondWith( fetch(e.request).catch(err=>{ return caches.match(e.request); }})));Copy the code

5. Cache strategy

  • Cachefirst Takes precedence over cache
  • Cacheonly cache only
  • Networkfirst Indicates the network priority
  • Networkonly network only
  • StaleWhileRevalidate is cached, updating the cache with network data

The complete sw.js is as follows:

SeriveWorker Cache_content const CACHE_NAME = 'cache_v' + 2; The longer the const CAHCE_LIST = [/ / list The easier problems'/', '/ index. HTML', '/ main js','/index. CSS ', '/ API/list', '/ manifest. Json, '/icon.png' ]; Async function fetchAndSave(request){let res = await fetch(request); // let cloneRes = res.clone(); // let cache = await caches. Open (CACHE_NAME); await cache.put(request,cloneRes); // Update the cache with the response result return res; } self.addeventListener ('fetch', (e) => {// let url = new url (e.equest.url); if(url.origin ! == self.origin){return} If (e.equest.url.includes ('/ API ')){return e.update with (fetchAndSave(e.equest).catch(res) => { return caches.match(e.request); } // serviceWorker does not support Ajax, Fetch with (fetch(e.equest). Catch (res => {return caches. Match (e.equest); }}))); // Skip waiting when serviceWorker is installed async function preCache() {let cache = await caches. Open (CACHE_NAME); // Create a cache space await cache.addall (CAHCE_LIST); await self.skipWaiting() } self.addEventListener('install', E.waituntil (preCache())}) async function clearCache() {let keys = await caches.keys(); return Promise.all(keys.map(key => { if (key ! == CACHE_NAME) { return caches.delete(key) } })) } self.addEventListener('activate', (e) => { e.waitUntil(Promise.all([clearCache(), clients.claim()])); // Give serviceWorker control immediately after activation})Copy the code

This is a complete process of pWA offline cache,

conclusion

1. Register with serviceWorker; 2, register listener function, divided into: request interception, installation, activation. Cache. Put, ** update view data; ** Install self.skipwaiting (); ** Install self.skipwaiting (); When activated, execute self.clients.claim() to give the current serviceWorker control and remove useless caches;

In actual project development, we only need to configure the workbox-webpack-plugin in webpack.

new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true })
Copy the code

The principle of PWA server message push is not explained here.