Featured image of post Web 2 Pwa

Web 2 Pwa

This is a summary of my experience and tutorial for adapting the site to PWA after today's exam.

This is after today’s exam, I summarize my experience and tutorial of making the site adapt to PWA.boring

What is PWA

PWA (Progressive Web App) is a technology and method used to develop web applications. PWA combine the best features of traditional web apps and native mobile apps to provide a better user experience.

PWA can be accessed through a browser and do not need to be installed on the device, but they have similar functionality and features to native apps. PWA make use of APIs provided by web browsers, such as Service Workers and Web App Manifest, to enable applications to work offline, push notifications, and add to the home screen. These features allow PWA to run in a consistent way across platforms and devices, without the need to download and install them from the app store.

Learn more about PWA

Check if your browser supports PWA

I recommend checking on caniuse.com to see if a browser supports PWA Link

When you open the website, the page looks like this ▽ caniuse.com 上的截图

Build a PWA

In short, build a PWA to a website can be done in the following steps:

  1. Full-site HTTPS
  2. Code manifest.json and sw.js
  3. Prepare app icons for 192×192px and 512×512px
  4. Bring in manifest.json files and sw.js files on relevant pages of your website

Next is my own practice.

manifest.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "name": "春树暮云", // the name that start screen displays
  "short_name": "春树暮云", // the name of the PWA
  "theme_color": "#f6ffff", // theme color
  "background_color": "#f6ffff", // background color
  "display": "fullscreen", // starting transition animation, standalone or fullscreen
  "scope": "/",
  "start_url": "/", // start page's URL
  "icons": [
        {
            "src": "192.png", // Rewrite according to the path of your icon
            "sizes": "192x192",
            "type": "image/png",
        },
        {
            "src": "512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
  ],

The above code can be changed according to the actual situation.


sw.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
'use strict' // Declare that the entire script is in strict mode
const cacheName = 'PWA-cache'; // the name of the cache
const startPage = 'https://example.com'; 
const offlinePage = 'https://example.com';
const filesToCache = [startPage, offlinePage];

const neverCacheUrls = [/admin/];// Never cache directories, such as the "Admin" folder in Typecho

self.addEventListener('install', function(e) {
  console.log('Cache event!')
  e.waitUntil(
    // When installing, cache the files that need to be cached
    caches.open(cacheStorageKey).then(function(cache) {
      console.log('Adding to Cache:', cacheList)
      return cache.addAll(cacheList)
    }).then(function() {
      console.log('Skip waiting!')
      return self.skipWaiting()
    })
  )
})

self.addEventListener('activate', function(e) {
  // Determine whether the address needs to be requested in real time, and if so, continue to send the request
  console.log('Activate event')
  e.waitUntil(
    Promise.all(
      caches.keys().then(cacheNames => {
        return cacheNames.map(name => {
          if (name !== cacheStorageKey) {
            return caches.delete(name)
          }
        })
      })
    ).then(() => {
      console.log('Clients claims.')
      return self.clients.claim()
    })
  )
})

self.addEventListener('fetch', function(e) {
  // When a cache resource is matched, the data is returned from the cache
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if (response != null) {
        console.log('Using cache for:', e.request.url)
        return response
      }
      console.log('Fallback to fetch:', e.request.url)
      return fetch(e.request.url)
    })
  )
})

Ingest files

Ingest manifest.json in the <head> tag.

1
<link rel="manifest" href="manifest.json" crossorigin="anonymous" />

Then, check whether the serviceWorker is registered in the tag <body>.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script type="text/javascript">
    if (navigator.serviceWorker != null) {
        navigator.serviceWorker.register('sw.js')
            .then(function(registration) {
                console.log('Registered events at scope: ', registration.scope);
            }).catch(function(err) {
                console.log('Registered events at fail: ', err);
            });
    }
</script>

At this point, the PWA has been adapted.

However, in blogging systems such as TypeCho or WordPress, it is easy to have unsuccessful adaptation due to various reasons. At this point, you can optionally guide the user to manually configure the PWA, for example, by clicking a button.

Here’s the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
  	var deferredPrompt = null;
  
		// 判断用户是否安装此应用:beforeinstallprompt,如果用户已经安装过了,那么该事件不会再次触发
  	// 需要卸载,然后重新打开浏览器才能再次触发
    window.addEventListener("beforeinstallprompt", e => {
      e.preventDefault();
      deferredPrompt = e;
    });
    
  	// 安装完成后触发,即点击安装弹窗中的“安装”后被触发
    window.addEventListener("appinstalled", () => {
      deferredPrompt = null;
    });
  
  
    function addToDesktop() {
      // 调用prompt()方法触发安装弹窗
      deferredPrompt.prompt();
      deferredPrompt = null;
    }
</script>
 
<button onclick="addToDesktop()">点击安装PWA</button>

The demonstration is as follows: Due to the replacement of the blog system, this link has failed, and I will try to configure PWA for this site when I have time in the future


Finally, to avoid compatibility issues, it is recommended that you place the manifest.json and sw.js files in the root directory of your website when deploying your PWA.

Licensed under CC BY-NC-SA 4.0