What is PWA?

PWA refers to Web applications developed using specified technologies and standard patterns, which give them the characteristics of both Web and native applications.

My simple understanding is: through some new technologies, enhance the user interaction experience of Web Apps.

You can install the app to make it work offline; Users are also more likely to access their favorite apps by clicking on ICONS on the home page rather than using a browser.

A standard PWA program must contain three parts:

  • HTTPS server or http://localhost
  • manifest.json
  • service worker

The advantage of the PWA

  • Progressive – Works with all browsers because it was developed with progressive enhancement in mind.
  • Smooth – Can be accessed offline or on a poor network with the help of the Service Worker.
  • Installable – Users can add common WebApps to their desktop without having to download them from the app store.
  • Native experience: It can be the same as app, with the first screen loading animation, can hide the address bar and other immersive experience.
  • Engagement – Users can return via push offline notifications, etc.

The core content of PWA

web app manifest

It is a JSON file and the manifest’s purpose is to install a Web application on the device’s home screen, providing faster access and a richer experience for the user.

Configure some information added to the surface.

{
  "name": "zh-llm-app"."shor_name": "zl"."start_url": "index.html"."icons": [{"sizes": "Width and height of ICONS added to desktop"."src": "ICONS added to desktop"."type": "Image/type"}]."background_color": "Background of application"."theme_color": "Navigation background"."display": "standalone"
  //fullscreen displays in fullscreen, all available display areas are used, and no status bar is displayed.
  // Standalone makes the app look like a standalone app, including different Windows, its own icon in the app launcher, etc.
  // minimal- UI The app will look like a standalone app, but will have a browser address bar.

}
Copy the code

For details, visit MDN: developer.mozilla.org/zh-CN/docs/…

sevice worker

Mainly used for persistent offline caching (this needs to be used with Caches). The Service Worker is a virtual agent between the browser and the network. The Service Worker runs on a thread separate from the main JavaScript thread of the page and has no access to the DOM structure. Control network requests, modify network requests, return cached custom responses, or synthesize responses. Is a special kind of Web worker.

What is a Web worker

The Web Worker is detached from the main thread, assigning complex and time-consuming tasks to it and telling the main thread through the postMessage method. The Web worker is an independent runtime environment that cannot manipulate the DOM and BOM.

One example: calculate a large sum. Because it is time-consuming, it is done outside the main js thread.

// work.js
    let total = 0;
    for (let i = 0; i < 100000000; i++) {
      total += i;
    }

    self.postMessage({
      total
    })
Copy the code
    console.log("start")
    const worker = new Worker("./work.js");
    worker.addEventListener("message".e= > {
      console.log("e", e.data)
    })
    console.log("end")
    
    // Execution order: start end Prints e
Copy the code

For detailed web worker usage, visit MDN: developer.mozilla.org/zh-CN/docs/…

Now let’s see how the powerful Sevice worker works

Register the service worker in window.onload to prevent competition with other resources. Registration service worker navigator. ServiceWorker. Register (“. / sw. Js’), returns a promise. After registration, the sw.js file is automatically downloaded, installed, and activated.

<script>
    window.onload = function() {
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('./sw.js')}}/* When the permission is default, we need to obtain the user's authorization. * /
    if (Notification.permission === 'default') {
      Notification.requestPermission()
    }
    if(! navigator.onLine) {new Notification('tip', { body: 'You don't have a network at the moment, you are accessing cached content! '})}/ / offline: break line
    window.addEventListener('online'.() = > {
      new Notification('tip', {
        body: 'Network connection, please refresh access to the latest data! '})})</script>
Copy the code

Service Worker life cycle events

  • Install.

In install’s listener function, we can initialize the cache and add files needed for offline applications.

// sw.js

// Define a cache_name variable to determine future cache file updates.
const CACHE_NAME = 'cache_v1';
// Listen for install events to cache static files
self.addEventListener("install".async (e) => {
  console.log("install==========")
  // Define a cache object.
  const cache = await caches.open(CACHE_NAME);
  // Cache files to cacheStorage
  await cache.addAll(['/'.'./01.jpg'.'./manifest.json'.'./index.css'])
  // The service-worker waits until the above code completes.
  await self.skipWaiting()
})
Copy the code
  • activate

This event is triggered when the service worker is activated and is used to delete old resources. When the service worker is activated, it takes effect the next time the page is refreshed, and you can immediately obtain control via self.clients.claim().

// sw.js

// Used to delete expired caches
self.addEventListener("activate".async e => {
  console.log("activate========")
  // Get all current cache objects
  const keys = await caches.keys();
  keys.forEach(item= > {
    if(item ! == CACHE_NAME) {// Delete the expired cache
      caches.delete(item)
    }
  })
  await self.clients.claim();
})
Copy the code
  • fetch

It is triggered every time an application makes an HTTP request. This event is very useful for us, allowing us to intercept requests and make custom responses to requests. Here we can choose network first or cache first.

// sw.js

self.addEventListener('fetch'.e= > {
  // 1. Only same-origin content is cached
  const req = e.request
  const url = new URL(req.url)
  if(url.origin ! == self.origin) {return
  }

  if (req.url.includes('/api')) {
  // Here is the request interface
    e.respondWith(networkFirst(req))
  } else {
  // Request static file in cache.
    e.respondWith(cacheFirst(req))
  }
})

// Cache priority, generally applicable to static resources
async function cacheFirst(req) {
  const cache = await caches.open(CACHE_NAME)
  const cached = await cache.match(req)
  // If obtained from the cache
  if (cached) {
    return cached
  } else {
  // Fetch can accept a URL or a request object.
    const data = await fetch(req)
    return data
  }
}

// Network priority data, if we get data, we should save a copy in the cache
async function networkFirst(req) {
  const cache = await caches.open(CACHE_NAME)
  try {
    const data = await fetch(req)
    // The network takes precedence. The data obtained should be updated to the cache again
    // Store a backup of the response in the cache
    cache.put(req, data.clone())
    return data
  } catch (e) {
    const cached = await cache.match(req)
    return cached
  }
}
Copy the code

For details, visit MDN: developer.mozilla.org/zh-CN/docs/…