PWA refers to progressive Web applications, generally consisting of Web App manifest, service worker, App shell, Notification push and other parts.

So far, only the Web App manifest and service worker are discussed here.

Web App Manifest makes applications installable. With the ability to add apps to the desktop via the browser, which can be accessed directly from the desktop, and the ability to define desktop ICONS, APP names, and system status bar colors, we can make our apps look like native apps. So as to give users a better experience.

From the product point of view, it will be more convenient for users to access our application from the desktop icon than through the browser. At the same time, it can also hide some controls of the browser itself, such as the navigation bar and the Settings at the bottom. And all this can be done for a small price. All you need to do is add a manifest.json file.

⚠️ Note: Before you jump on the bandwagon, make sure your app is site-wide HTTPS. If not, it’s best to wait until your application is fully HTTPS.

Web App Manifest

Let’s look at what the manifest.json file looks like:

The Web App Manifest document

{
  "name": "Front-end collector"."short_name": "fedaily"."orientation": "portrait"."display": "standalone"."start_url": "/"."description": "Collect only the good, recommend only the good."."background_color": "# 000"."theme_color": "# 000"."icons": [{"src": "icon.png"."sizes": "96x96"."type": "image/png"} / /... You can add ICONS of various sizes]}Copy the code

For iOS platform, we need to add:

IOS Application official documentation

<link rel="apple-touch-icon" href="icon.png">
<meta name="apple-mobile-web-app-title" content="Front-end collector">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="theme-color" content="# 000000">
Copy the code

Then import the JSON file into your HTML.

<link rel="manifest" href="/manifest.json">
Copy the code

⚠️ Note that this needs to be in the project root directory, otherwise Android will not automatically pop up the add to the desktop prompt (you may ask, does iOS have this problem, no, iOS does not support this feature).

Once you’ve done that, you’ll have an application that can be installed on your desktop.

You can access your app from Android’s Chrome browser, and then add your app to the desktop using the Settings – Add to Desktop feature. You can access your app using the iOS Safari browser and add your app to the desktop by clicking the Share button at the bottom – Add to home screen.

Service Worker

A Service worker is an event-driven worker registered with a specified source and path. It uses JavaScript to control associated pages or websites, intercepts and modifies access and resource requests, and cache resources in a fine-grained manner. You have complete control over how your application behaves in certain situations, most commonly when the network is unavailable.

The Service worker runs in the worker context, so it does not have access to the DOM. It runs in a different thread than the main JavaScript thread that drives the application, so it doesn’t block. It is designed to be fully asynchronous, and synchronization apis such as XHR and localStorage are not available in service workers.

For security reasons, Service workers can only be hosted by HTTPS because the ability to modify network requests is too dangerous to expose to man-in-the-middle attacks. In the User privacy mode of Firefox, the Service Worker is unavailable.

The specific API and detailed introduction of service worker can be found on the Internet. All of them are very detailed. Here is a very convenient method.

The Workbox WebPack Plugin can be integrated with WebPack and most of the functionality we need can be achieved with minimal configuration.

Let’s see how it works:

new GenerateSW({
  swDest: 'sw.js'.skipWaiting: true.clientsClaim: true.cleanupOutdatedCaches: true.inlineWorkboxRuntime: true.runtimeCaching: [{
    handler: 'NetworkFirst'.urlPattern: /\/api/.method: 'GET',}})Copy the code

The plugin reads static resource files built by WebPack itself, such as.js,.css,.png, etc., and adds them to the cache list, so there is no need to specify them here.

Through runtimeCaching, we can cache, for example, API return values. Here, ⚠️ suggests careful consideration. If you do not care about the experience of your application when it is disconnected from the network (as a Web application, in most cases, nothing can be done when it is disconnected from the network. Inappropriate caching strategies can lead to unexpected bugs 🐛)

This plugin will automatically generate the service worker file according to your configuration, isn’t it easy? Then you need to register the Servie worker in your HTML.

<script>
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
      navigator.serviceWorker.register(`/sw.js?The ${Date.now()}`).then(registration= > {
        console.log('SW registered: ', registration)
      }).catch(registrationError= > {
        console.log('SW registration failed: ', registrationError)
      })
    })
  }
</script>
Copy the code

⚠️ Note: the sw.js file, like manifest.json, also needs to be placed in the project root directory.

At this point, we actually have a basic pWA application that we can use.

If you need more customization, you can read the Workbox documentation to implement it.

By setting mode to ‘development’, you can generate an uncompressed sw.js file for easy reading of the generated code.

Problems encountered during development

Android Chrome does not add pop-ups to desktop pop-ups

Check if your manifest.json, sw.js files are in the project root directory. And whether the SW is successfully registered.

Android was slow to add to the desktop

No good solution has been found yet

How to determine whether to access from the home screen

We need to distinguish between the user accessing from the browser or from the home screen due to buried points and other requirements.

export const isFromDesktop = () => {
  // window.navigator.standalone used for safari
  if (window.navigator.standalone) {
    return true;
  }
  if (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) {
    return true;
  }
  return false;
};
Copy the code

How do I listen for events added to the desktop

let deferredPrompt
window.addEventListener('beforeinstallprompt', event => {
  console.log('install event trigger', event);
  // Disable the browser to automatically display the add to the desktop prompt
  e.preventDefault();
  // Store event objects for later custom popup timing
  deferredPrompt = e;

  // Then you can display a custom prompt box to the user, telling the user
  // Can you add the application to the desktop and tell me how to do it
})

// Listen for the button click event in the custom prompt box
buttonInstall.addEventListener('click', (e) => {
  // Hide our custom popover
  hideMyInstallPromotion();
  // Display added to desktop prompt (browser provided)
  deferredPrompt.prompt();
  // Here you can get the user's choice and do some operations related to buried points
  deferredPrompt.userChoice.then((choiceResult) = > {
    if (choiceResult.outcome === 'accepted') {
      console.log('User acceptance');
    } else {
      console.log('User rejected'); }})});Copy the code

Failed to add to the home screen

Android browser is added to the main screen permission may not be opened, if you don’t open, also won’t have to add to the main screen that prompt box, need to open access to try again (so a custom prompt box is very important, can inform users how to deal with failure, after the test, because authority leads to failure and there is no hint to the user)

How do I capture installation success action

window.addEventListener('appinstalled', (evt) => {
  console.log('app installed', evt);
})
Copy the code

How to Log out of sw

// Registrations is a callback to the successful registration
for (let registration of registrations) {
  registration.unregister()
}
Copy the code

How to downgrade

You are advised to configure an interface and decide whether to register the SW based on the interface. Otherwise, the existing SW is deregistered. This makes it easy to deal with online problems. Such as:

fetch('/api/sw/').then(result= > {
  if (result) {
    // Register operation
  } else {
    // Log out}})Copy the code

Writing this, it seems that the actual development process is quite a lot of problems, but all in all, PWA is well worth trying. After all, the experience is quite different.

Later encounter other questions to update, also welcome you to actively leave a message exchange…

Public number: Front-end collector. Collect excellent front-end technology information from the whole network, share with you, and grow together.