存档 · 2021年6月9日 0

使用 PWA 其中两个功能

PWA?

PWA 全称 Progressive Web App,即渐进式 WEB 应用。

一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用. 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能
解决了哪些问题?

  • 可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏;
  • 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能;
  • 实现了消息推送

它解决了上述提到的问题,这些特性将使得 Web 应用渐进式接近原生 App。

—— segmentfault.com

Manifest 添加至主屏幕

在首页 <head> 中添加:

<link rel="manifest" href="/manifest.json" />

在站点根目录 vi manifest.json

{
    "name": "Cusalt! • 应用程序",
    "short_name": "Cusalt!",
    "description": "Web App for Site Cusalt!",
    "display": "standalone",
    "start_url": "/",
    "theme_color": "#009688",
    "background_color": "#009688",
    "icons": [
        {
            "src": "icon/appicon.png",
            "sizes": "512x512"
        }
    ]
}
  • "name": 必填,显示应用名称;
  • "short_name": 可选,在 APP launcher 和新标签页显示。如果没有设置,则使用 "name": 的值;
  • "background_color": 在启动 web 应用程序和加载应用程序的内容之间创建了一个平滑的过渡。

mkdir iconcd icon,放入文件应用图标 appicon.png。

这时由于没有启用 Service Worker,Manifest 也没有作用。

在首页 <body><footer> 中添加:

    <script>
      // 注册 service worker
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/service-worker.js', {scope: '/'}).then(function (registration) {
          // 注册成功
          console.log('ServiceWorker registration successful with scope: ', registration.scope);
        }).catch(function (err) {
          // 注册失败 :(
          console.log('ServiceWorker registration failed: ', err);
        });
      }
    </script>

在站点根目录 vi service-worker.js

self.addEventListener('install', event => {
    console.log('install',event);
    event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', event => {
    console.log('activate',event)
    event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', event => {
    console.log('fetch',event)
});

完成。去浏览器清除缓存,你试试地址栏右边是不是多了个安装应用的按钮呢?

你也可以 f12 到 Application 栏进行调试。

Service Worker 缓存

Service Worker 高级在于它能离线缓存和动态缓存。

vi service-worker.js

// 版本修改的时候会触发 activate,将旧版本的缓存清理掉。
var OFFLINE_PREFIX = 'offline-';
var CACHE_NAME = 'main_v1.0.0';

var cacheName = 'helloWorld';    // 缓存的名称
// install 事件,它发生在浏览器安装并注册 Service Worker 时。
self.addEventListener('install', event => {
    /* event.waitUtil 用于在安装成功之前执行一些预装逻辑
     但是建议只做一些轻量级和非常重要资源的缓存,减少安装失败的概率
     安装成功后 ServiceWorker 状态会从 installing 变为 installed */
    event.waitUntil(
        caches.open(cacheName)
            .then(cache => cache.addAll([    // 如果所有的文件都成功缓存了,便会安装完成。如果任何文件下载失败了,那么安装过程也会随之失败。
                '/index.php'
            ]))
    );
    console.log('install', event);
    event.waitUntil(self.skipWaiting())
});

/**
为 fetch 事件添加一个事件监听器。接下来,使用 caches.match() 函数来检查传入的请求 URL 是否匹配当前缓存中存在的任何内容。如果存在的话,返回缓存的资源。
如果资源并不存在于缓存当中,通过网络来获取资源,并将获取到的资源添加到缓存中。
*/
self.addEventListener('fetch', function (event) {
    console.log('fetch', event);
    event.respondWith(
        caches.match(event.request).then(function (response) {
            if (response) {
                return response;
            }
            var requestToCache = event.request.clone();
            return fetch(requestToCache).then(
                function (response) {
                    if (!response || response.status !== 200) {
                        return response;
                    }
                    var responseToCache = response.clone();
                    caches.open(cacheName)
                        .then(function (cache) {
                            cache.put(requestToCache, responseToCache);
                        });
                    return response;
                })
        }
        )
    )
});

self.addEventListener('activate', event => {
    console.log('activate', event)
    event.waitUntil(self.clients.claim());
    var mainCache = [CACHE_NAME];
    event.waitUntil(
        caches.keys().then(function (cacheNames) {
            return Promise.all(
                cacheNames.map(function (cacheName) {
                    if (mainCache.indexOf(cacheName) === -1 && cacheName.indexOf(OFFLINE_PREFIX) === -1) {
                        // When it doesn't match any condition, delete it.
                        console.info('SW: deleting ' + cacheName);
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
    return self.clients.claim();
});

至此,PWA 这两个功能已经启用完毕。剩下唯一可能要自定义的,是上方的第 14 行。