background

PWA (Progressive Web App) is a next-generation Web App concept proposed by Google at Google I/O in 2016. It was launched in 2017 to enhance Web experience. Narrow the gap between Web apps and Native apps and create a similar user experience. At present, Chrome, Safari, Firefox and Edge browsers all support PWA to varying degrees. Some websites at home and abroad have implemented PWA, such as Twitter, Starbucks, Ele. me and Sina, among which there are many successful cases. There are some quantifiable benefits after applying PWA:

  • After West Elm implemented PWA, the data showed a 15% increase in user time and a 9% increase in revenue.
  • Flipkart, India’s largest e-commerce site, saw a threefold increase in user time and a 40% increase in interaction.

Theoretically, for all Web apps, PWA can be realized as long as they are transformed by referring to THE STANDARD of PWA. For APPS, user experience is an important criterion to judge whether an application is good or bad. The transformation of PWA is not complicated, but can greatly improve user experience, which is a technology with a high input/output ratio. In addition, PWA’s related functions and browser support for PWA are also increasing, which has great potential in the future.

But at present, due to compatibility and other problems have not been widely applied to practice, and the online PWA practice articles are scattered, we in Sharee project mobile terminal transformation OF PWA, in this paper, introduced the main functions and implementation of PWA, and recorded some problems encountered in the implementation process. I hope this article can help you understand this technology and the problems encountered in the process of practice.

The selection

As we all know, Web App is far inferior to Native App in user experience, because Web App is strongly dependent on the network, and has problems such as slow loading speed in weak network environment and inaccessible offline condition. Moreover, Web App cannot be separated from browser UI, and the user’s visual area is compressed. You can’t install it on your mobile desktop. Although Native App has many advantages, it also has many problems: high development cost, maintenance of multiple versions of update and upgrade, and closed content, which is not conducive to App promotion, users need to download the installation package before using.

As Web App developers, we are more concerned about how to add features of Native App to Web App, and PWA comes into being.

App type

The existing App types in the market can be divided into the following four types. By comparing their performance in different indicators, we can see that PWA is still a Web App in essence, but it is closer to Native App than Web App in terms of performance. Although we have made a comparison with Native App here, the purpose of PWA is not to replace Native App or compete with Native App, but to upgrade Web App and bring better user experience to users.

PWA(Progressive Web App)

Now that we know that PWA is used to improve the user experience, here is the official definition of PWA.

define

Progressive Web Apps use modern web capabilities to deliver an app-like user experience. They evolve from pages in browser tabs to immersive, top-level apps, maintaining the web’s low friction at every moment.

As we can see from the definition, PWA is not a single technology, but a Web App that applies multiple technologies, and is essentially a Web App. PWA uses some new technologies to combine the advantages of Web App and Native App, so that users can feel that they are using Native App when using Web App.

The characteristics of

PWA is fast, reliable and sticky. Fast is fast response, through an independent thread for resource caching, improve page loading time; Reliability means that the App can load and display content instantly even in an unstable network environment and provide effective feedback to users even in offline environment. Engagement is about enhancing user engagement through immersive user interfaces, desktop ICONS, and notifications.

Standard support

According to the statistics of Can I Use (including PC and Mobile), the compatibility of all technologies involved in the realization of PWA is as follows. The most important APP Manifest and Service Worker are well supported and are supported in mainstream browsers and operating systems:

  • App Manifest has 57.43% support
  • Service Worker support reaches 72.82%
  • The Notifications API has 43.3% support
  • The Push API has 72.39% support
  • Background Sync is not available. Chrome 49 supports Background Sync

purpose

Implement PWA on Sharee mobile with the expected goal of optimizing user experience and increasing user retention and visits.

The optimization of user experience mainly includes the following two aspects:

  • Add pocket page in offline environment: Return to the pocket page to avoid a web crash in the browser when a user request fails.
  • Improve resource loading speed during cold startup: precache static resources and save them in the Local Storage. During cold startup, use the Service Worker to respond, avoiding 304.

Retention and visits increase through two things:

  • For web users to increase the resident entrance, reduce the depth of the site entrance.
  • Push message: Push message is also a way to attract users to visit, but push message needs operation and maintenance. Meanwhile, the main body of Sharee’s push message is still a Native App, so this function has not been implemented yet.

indicators

Technical landing requires the quantification effect of data indicators. The following data are collected for long-term observation:

  • Click, install, cancel permeability of PWA Install
  • Percentage of visits to PWA entry (desktop icon)
  • Static resource load time

plan

This section is a detailed introduction to the implementation details of PWA. Firstly, the benefits generated by App Manifest, Service Worker and Push API, which are the main technologies PWA relies on, and their concrete implementation are introduced. Then, the best practice Workbox of PWA recommended by Google is introduced. Finally, I briefly introduced how to test the PWA. The overall structure is shown in the figure.

App Manifest

The App Manifest is a JSON-formatted file used to configure information about a web application. This file allows you to configure information about desktop retention ICONS, installing pop-ups, and starting animations.

On-screen retention icon

We can configure the icon and name of the desktop retention icon in the configuration file. When the user saves the website on the desktop, the configuration information will be automatically applied.

earnings

The benefits of adding to the home screen are many, mainly in terms of engagement and user experience. Desktop ICONS reduce the depth of entry to the site, allowing users to access the site directly from the home screen rather than from the first page of the browser. ICONS added to the home screen are close to Native App experience, as shown below. The second from the left is Native App, and the third from the left is PWA. Other Native apps are all:

Websites that are accessed from desktop ICONS have a launch page and a full-screen experience that is detached from the browser UI, and sites that are added to the home screen are included in the app drawer. Add screen ICONS without downloading, similar to desktop shortcuts, reducing the cost of App installation.

But PWA’s on-screen ICONS are different from shortcuts.

Differences with shortcuts:

  1. The on-screen retained icon has a separate icon and name.
  2. Click on the icon to open the site. Instead of a blank screen, the loading process is replaced by a launch page showing the app’s icon and name. The loading page disappears at the end of the loading process.
  3. When the web page is finally displayed, browser elements such as the address bar and toolbar will not be displayed. The web page content will fill the screen and look the same as Native App.

implementation

The Mainfest file uses a text description that specifies what is displayed on the desktop and asks the user if they want to add it. After adding it, you can check your Mainfest file in The Chrome application-Manifest to see if it works.

Configuration items: web.dev/add-manifes…

Example configuration file:

// manifest.json {"name": "Sharee PWA", // used to install banner, splash screen display "short_name": "Sharee PWA", // for the home screen display // The browser selects from sizes field of the valid icon. First look for "ICONS" that match the display density and size to 48dp screen density: [{" SRC ": "../logo/180.png", "sizes": "180x180", "type": "image/png" }, { "src": "../logo/192.png", "sizes": "192x192", "type": "image/png" }, { "src": "../logo/512.png", "sizes": "512x512", "type": "image/png" } ], "start_url": "/? From =homescreen", // start url, relative to manifest.json path "scrope": "/", // sw scope can only be in this path or subpath "display": "standalone", "theme_color": "#FFF", "background_color": // iOS does not support manifest configuration, <meta name=" Apple-mobile-web-app-capable "content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="#fff"> <meta name="apple-mobile-web-app-title" content="Sharee PWA"> <link rel="apple-touch-icon" sizes="180x180" href=".. /logo/180.jpg"> <meta name="msapplication-TileColor" content="#fff"> <meta name="theme-color" content="#fff" />Copy the code

To install Windows

After the website adds the Manifest configuration file and meets certain requirements, the browser will pop up a pop-up window at an appropriate time to ask whether the user needs to add a screen icon according to the user’s access frequency, as shown in the following figure.

earnings

The installation window is mainly used to guide users to save screen ICONS and improve the adding rate of screen ICONS.

implementation

The application installation prompt displayed on the browser must meet the following conditions: web.dev/install-cri… :

  1. Has not yet been installed

  2. Access via HTTPS (in debug mode http://127.0.0.1 or http://localhost is allowed)

  3. The manifest.json file contains the following configuration:

    1. Name/short_name: Short_name is preferred
    2. start_url
    3. icons
    4. The display forstandalonefullscreenminimal-ui
  4. The site must register the Service Worker

    1. Chrome requires Service workers and must listenfetchThe event
  5. User visits frequently enough (the browser doesn’t specify how often)

Developers cannot actively trigger the pop-up of the installation prompt, but can listen to the beforeInstallPrompt event to intercept the popover event and save it, and then provide button triggering:

let appPromptEvent = null; const installBtn = document.getElementById('install-btn'); self.addEventListener('beforeinstallprompt', function(e) { e.preventDefault(); // Save the popover event appPromptEvent = event; installBtn.classList.add('visible'); return false; }); Window.addeventlistener ('appinstalled', function () {console.log(' appinstalled'); installBtn.classList.remove('visible'); }); installBtn.addEventListener('click', function () { if (appPromptEvent ! == null) {console.log(appPromptEvent) // Trigger popup apppromptevent.prompt (); AppPromptEvent. UserChoice. Then (function (result) {if (result. The outcome = = = 'accepted') {the console. The log (' agreed to install applications'); } else {console.log(' do not agree to install application '); } appPromptEvent = null; }); }});Copy the code

Beforeinstallprompt compatibility is as follows, most browsers do not support popovers. Actually in support of the browser window, it trigger strategy is also very low frequency, users need in a short period of time to interact with web site for high frequency would trigger, and the user choose cancel after installation will not trigger again for a long time, the browser needs to ensure that the user experience, no one will like frequent pop-up ads influence their views:

Start the animation

When entering from the screen icon, the startup animation is automatically generated according to the configuration items in the Manifest file to transition the blank screen time before resource pulling and improve user experience. The startup interface is as follows:

earnings

Start animation can optimize interaction, use the time of the first screen to display effective content, deepen users’ memory of the website, and ease the waiting time, which is like a Native App in terms of experience.

implementation

The startup animation is automatically generated according to the configuration items in manifest.json. The corresponding relationship between configuration items and the animation interface is shown in the following figure:

Service Worker

A Service Worker is a Worker thread that is independent from the main thread of the browser and can run in an offline environment. It is completely isolated from the main thread of the current browser and has its own independent execution context. HTML5 provides a Service Worker API, which can register and cancel Service Worker threads. In addition, once the Service Worker is successfully installed, it will always exist unless the thread is actively removed by the program. In addition, the Service Worker can be directly activated when accessing the page. If the browser or browser label is closed, it will automatically sleep to reduce resource consumption. Using these features of the Service Worker, we can pre-cache offline pages and static resources.

Offline Page

In the case of user disconnection, the network crash page of the browser usually appears, giving people the impression of poor accessibility of the App. Through Service Worker, we can pre-cache a offline static page when users visit the website for the first time, and return the page when the request fails to be monitored, so as to improve user experience.

earnings

Offline pages can improve user experience when there is no network. App shell can also be cached for websites with app shell structure.

implementation

A Service Worker is a script that the browser runs independently of the web page in the background, a proxy server between the browser and the server. Browsers support the following:

The life cycle of Service Worker is shown in the figure. We can write different codes in the corresponding life cycle to automatically trigger execution:

install

We precache the offline page at the point where a precached file is normally added at installation time. Caches are a special CacheStorage object that provides the ability to store data within the scope specified by the Service Worker. Here, we open a cache with a given name and add all the files our application needs to cache.

const CACHE_NAME = 'sharee-v1'; const FILES_TO_CACHE = ['offline.html']; // Install SW self.addEventListener('install', e => { console.log('[Service Worker] Install'); E.waituntil (caches.open(CACHE_NAME).then(function(cache) {self.skipwaiting (); // If a new service worker file is detected, the old return cache.addall (FILES_TO_CACHE) is immediately replaced; })); });Copy the code
fetch

After successful registration, monitor all requests of the website, and intercept specific requests by the Service Worker accordingly. The respondWith method takes over response control and acts as a proxy service between the server and the application. It allows us to respond whatever we want to each request.

Self. addEventListener('fetch', Match (e.equest). Then (function(r) {console.log(' [Service Worker]] Fetching resource: ${e.request.url}`); Return (r | | / / the cache without the request data from the network and cache the fetch (" e.r equest). Then (function (response) {return caches.open(CACHE_NAME).then(function(cache) { console.log( `[Service Worker] Caching new resource: ${e.request.url}`, ); cache.put(e.request, response.clone()); return response; }); })); })); AddEventListener ('fetch', e => {e.espondwith (caches. Match (e.equest, {mode: 'cors' }).then( () => { return fetch(e.request).catch(() => caches.match('offline.html')); },),); });Copy the code
activate

Activate is usually used to delete files that are no longer needed or to do some cleaning. When we update the version number to V2, the Service Worker adds all of our files (including the new ones) to a new cache. At this point, a new Service Worker will be installed in the background, and the old Service Worker will continue to run correctly until no pages are using it, at which point the new Service Worker will be activated and take over all pages.

self.addEventListener('activate', Function (e) {e.waituntil (// remove old caches.keys(). Then (function(keyList) {return Promise.  { if (CACHE_NAME.indexOf(key) === -1) { return caches.delete(key); }})); })); });Copy the code

A resource cache

Resource cache also makes use of Service workers to cache specific responses in the FETCH stage, and then directly return to the cache when the same request is monitored next time to improve response time and reduce server pressure.

earnings

We cache all static resources that are less updated through the regular match of the request path, cache them when the user first visits the site, and update them from the cache afterwards to improve cold start response time.

implementation

const cacheList = [ '/static/css/', '/static/js' ]; self.addEventListener('fetch', e => { const cached = cacheList.find(c => { return e.request.url.indexOf(c) ! = = 1; }); if (cached) { e.respondWith( caches.match(e.request).then( function(r) { return ( r || fetch(e.request).then(function(response) { return caches.open(CACHE_NAME).then(function(cache) { if (cached) { cache.put(e.request.url, response.clone()); } return response; }); })); },),); });Copy the code

Push API & Notification API

PWA also provides apis to Push messages to users on the web site, usually the Push API and Notification API.

Push notification

earnings

PWA provides a message push that has many advantages. First, it can attract users to visit it. And the message push as long as the browser is running, without the user to open the web page; User authorization is required for notification push, but only one authorization is required for web pages under the same domain name.

implementation

Push Notifications are assembled using two apis: the Notifications API and the Push API. The Notifications API enables applications to display system Notifications to users. The Notification and Push apis are built on top of the Service Worker API, which responds to Push message events in the background and relays them to the application.

Notification API

The Notification API is a new desktop Notification API for HTML5 that displays notifications to users.

Self. Registration. ShowNotification (' PWA - Book - Demo test actions, {body: 'thumb up button to click on' actions: [{action: 'like', the title: "Thumb up" icon: '/ assets/images/like - icon. PNG',},],}); Self.addeventlistener (' notificationClick ', function(e) {// close notification e.novel.close (); If (e.action === 'like') {// click the 'like' button console.log(' like'); } else {// Click on the rest of the dialog console.log(' click on the dialog '); }});Copy the code
Push API
Self. addEventListener('push', function (e) {if (! E.data.text () // Generate desktop notifications based on push messages and display them. Let promise = self.registration.showNotification(payload.title, { body: payload.body, icon: payload.icon, data: { url: Payload. Url}}) e.waituntil (promise)}) self.addeventListener (' notificationClick ', E.waituntil (self.clients.openwindow (e.data.url))}) function (e) {// close e.novel.close () // open e.waituntil (self.clients.openwindow (e.data.url))})Copy the code

The browser compatibility of the two apis is as follows:

Workbox Best Practices

Workbox is a set of PWA solutions launched by Google Chrome team. This solution contains core libraries and construction tools. We can use Workbox to achieve rapid development of Service workers. The definition is as follows:

Workbox is a library that bakes in a set of best practices and removes the boilerplate every developer writes when working with service workers.

The function of Workbox is very perfect, and the plug-in mechanism can well meet the needs of various business scenarios. If you manually maintain the original Service Worker file of an application, the workload is huge, and many potential problems are not easy to be found. Workbox can well avoid many potential problems of Service workers and greatly reduce the maintenance cost of Service workers. Therefore, it is recommended that you give priority to Workbox when you start to consider using Service workers. Here’s how to use Workbox.

The introduction of Workbox

Once the Workbox is loaded, we can use various functions mounted on the Workbox object:

ImportScripts (' https://storage.googleapis.com/workbox-cdn/releases/4.2.0/workbox-sw.js')Copy the code

Pre cache

The precached files in PWA are simply given the file path as shown in the following interface, and will be precached on the first visit to the site:

/ / cache, with the ws cacheName workbox. Core. SetCacheNameDetails ({prefix: 'sharee, suffix:' v1 ', precache: 'precache, runtime: 'runtime'}) / / dynamic cache workbox. Routing. PrecacheAndRoute ([{url: '/ index. HTML, revision: 'asdf' }, '/index.abc.js', '/index.bcd.css' ])Copy the code

Route match & request response

Workbox manages resource request matching and cache policy execution in a unified way, using the following function to standardize dynamic caching by organizing the route registry:

workbox.routing.registerRoute(match, handlerCb)

Match: indicates the route matching rule

Match in the above function refers to the routing matching rule, and there are three matching methods.

  1. Matches urls with strings, absolute path/relative path
Workbox. Routing. RegisterRoute (' http://127.0.0.1:8080/index.css 'handlerCb) workbox. Routing. RegisterRoute ('/index. The CSS, HandlerCb) / / workbox. Based on the current url routing. RegisterRoute ('. / index. CSS ', handlerCb)Copy the code
  1. Regular match

workbox.routing.registerRoute(``/\/index\.css$/``, handlerCb)

  1. Customize the matching method

This custom method is a synchronous execution function that returns a true value when indicating that the resource request was matched successfully.

const match = ({url, event}) => {
  return url.pathname === '/index.html';
}

Copy the code

HandlerCb: Resource request processing method

This is where the developer decides how to respond to the matched resource request, whether from the network, from the local cache, or directly from the Service Worker. This method is asynchronous and returns a Promise, requiring that the result of the Promise resolution must be a Response object.

// url: event.request.url The object instantiated by the URL class; // event: fetch event callback parameter; // params: value returned by the custom route matching method. const handlerCb = ({url, event, params}) => { return Promise.resolve(new Response('Hello World! ')); }Copy the code

Caching strategies

After intercepting the request, we may cache the response. Usually, we need to write our own policies. Workbox provides several common policies that can be used directly. The Workbox. Strategies object provides a set of commonly used dynamic caching strategies for processing resource requests, including the following five:

  • NetworkFirst: NetworkFirst
  • CacheFirst: CacheFirst
  • NetworkOnly: Uses only normal network requests
  • CacheOnly: Only resources in the cache are used
  • StaleWhileRevalidate: Sends a network request to update the local cache while reading a resource from the cache

Using CacheFirst as an example, we can use it as follows:

workbox.routing.registerRoute( /\.(jpe? g|png)/, new workbox.strategies.CacheFirst({ cacheName: 'image-runtime-cache', plugins: [/ / through plug-ins to strengthen caching policy new workbox. The expiration, the Plugin ({maxAgeSeconds: 7 * 24 * 60 * 60, / / the image resource cache maxEntries 1 week: FetchOptions: {mode: 'cors'}})Copy the code

The plug-in

Workbox also provides some plug-ins using the Workbox API, such as the cache expiration time “developers.google.com/web/tools/w…” :

Each time a request to the cache is used or updated, the plug-in looks at the cache in use and removes any old or redundant requests.

When used with maxAgeSeconds, requests can be used once after expiration, because expiration cleanup is not done until cached requests are used.

workbox.routing.registerRoute(
  new RegExp('.*/static/.*'),
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: CACHE_NAME,
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxAgeSeconds: 7 * 24 * 60 * 60,
      }),
    ],
  }),
);

Copy the code

Test the PWA

Typically, we use Chrome Lighthouse to test a site’s PWA implementation, select Progressive Web App in the Audits of the Chrome browser, run tests, and generate test reports. The PWA test report for Sharee is provided in the figure below, which is evaluated from several aspects.

Problems encountered in the online environment

After we launched, we encountered some problems that did not exist in the test environment. We recorded these problems and their solutions for your reference in the development process.

  1. Failed to cache resources

First consider the request interception phase, whether it is due to cross-domain requests.

The Workbox requires a match with the regex of the same server. If it is a same-origin request, the regular expression will match as long as the requested URL matches. If it is a cross-domain request, the regular expression must match the beginning of the URL.

Eg. If you want to match abc.def.com/aaa/bbb/sta… Use regular expressions to new RegExp (‘ https://abc\\.def\\.com. * / static / | js (CSS) /. * ‘).

Then consider whether the cache phase after successful request interception is caused by cache policy. CacheFirst does not cache opaque responses. The Type of the opaque Response is Response-type: Opaque. To avoid cross-domain information leakage, a large amount of fill is added to the size of the opaque Response during storage space calculation. The fill size varies with browsers.

  1. SecurityError, the detailed error information is as follows
SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported: b.setattribute ('href', e.canvas.todataURL ());Copy the code

Function HTMLCanvasElement. ToDataURL () will the canvas to a PNG image, returning URL base64 format data. If your image URL is not in the same domain as the page, a security error will be reported when you call toDataURL(). You can use images in the canvas without CORS authorization, but doing so will pollute the canvas. As long as the canvas is polluted, data can no longer be extracted from the canvas and the toDataURL() method can no longer be called, otherwise a Security error will be raised. This is actually to protect users’ personal information from unauthorized loading of users’ images from remote Web sites, resulting in privacy leakage.

Set image.setAttribute(‘crossOrigin’, ‘anonymous’) to access-Control-allow-headers: *. , and finally update the online pwacompat.min.js file. Because the pwacompat.min.js file is pre-cached and has an expiration time, the file name and cached version need to be updated to ensure that all users update the file.

Join us

We are bytedance’s international team, mainly responsible for the r&d of bytedance’s overseas products. The team has rich experience in r&d of overseas products, and has the technical framework to support a large number of users. Our technology stack covers a wide range, from H5 inside the end to Web and Wap outside the end. Compared with the conventional front-end business, there are richer business landing scenes: community, games, live broadcasting, activities, creation and so on. Technically, it has its own independently maintained infrastructure, plays with front-end basic engineering, and implements the best practices of modern front-end development. It also has the underlying kernel applications such as video, image and rich text, as well as the forward-looking technologies such as PWA, SSR, front-end microservices, Serverless and edge computing. The range of technology growth options is very wide, and there is a lot of room for growth.

At the same time, the team has established RESEARCH and development teams in many places around the world, inclusive of diverse cultures, and enjoy the experience of multinational research and development team cooperation. If you join us, you will have the opportunity to exchange technical experience with engineers from different countries, and experience different culture and life in different places.

Contact: [email protected]; Email subject: Name – years of service – Front-end internationalization.

A link to the

  1. Standard support query

caniuse.com/

  1. App Manifest configuration item

Web. Dev/add – manifes…

  1. Browser installation pop-up pop-up conditions

Web. Dev/install – cri…

  1. Notification push API

Developer.mozilla.org/en-US/docs/…

Developer.mozilla.org/en-US/docs/…

  1. Service Worker API

Developer.mozilla.org/en-US/docs/…

  1. Workbox plugin: Cache expiration time

Developers.google.com/web/tools/w…

  1. PWA Demo provided by Google

airhorner.com/

Github.com/GoogleChrom…

  1. IOS profile compatibility solution

Github.com/GoogleChrom…

  1. Workbox Best Practices

Github.com/GoogleChrom…


Welcome to “Byte front end ByteFE”

Resume mailing address: [email protected]