# PWA

PWA(Progressive Web App)是指 “渐进式Web应用”。

兼容性: iOS(11.3+)、Chrome for Android(40.0)

# 和 Web App的区别

PWA 具有 “渐进增强” 的特点:

  • 离线存储

  • 通知推送

  • 桌面访问

以上的功能的核心都是 Service Worker

# Service Worker

说明
定义 相当于一个 Proxy,用于 监听、管理“请求和响应”
运行环境 运行在一个 单独的线程 下
生命周期 1. 当 “浏览器进程” 关闭后,Service Worker线程 会销毁
2. 当 “网页” 关闭后,Service Worker线程 不会被销毁
作用 仅对 httpshttp://localhost 有效

用法

// 注册Service Worker

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/m/music-mobile-sw.js');
}

// 通过 `self` 访问全局上下文
// 监听 “注册完成” 事件
// 用处:一般用来创建 cache实例,(使用 “离线缓存” 能力)
self.addEventListener('install', event => {});

// 监听 “激活完成” 事件
// 用处:一般用来更新缓存文件
self.addEventListener('activate', event => {});

// 监听 “请求” 事件(仅针对那些被 service worker 控制的资源 才会触发)
// 用处:一般用来拦截请求,当 请求失败 时从 cache 里取
self.addEventListener('fetch', event => {});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 离线存储

原理:在 Service Worker 线程下,调用 cacheStorage 的 Api,从而实现 更精细化地 控制缓存。

虽然 cacheStorage 定义在 Service Worker 规范下,但也可以不一定在下面用。只是搭配 Service Worker 的各个时机可以更好地控制缓存。

# 具体步骤

TIP

window.caches 可获取 CacheStorage对象

  • open(version): 创建/获取 指定版本下 的 Cache实例
  • keys(): 获取所有 Cache实例 的版本
  • delete(version):删除 指定版本下 的 Cache实例
  • match(request):检查给定的 request 是否为 Cache实例 跟踪的 request

cacheCache实例

  • addAll([path1, path2...]): 让 Cache实例 跟踪指定的 path
  • push(request, response): 将 request、response 添加到 Cache实例

通过 window.caches.open(cacheName) 可以获取/创建相应的 Cache实例

一般是在 Service Worker 作用域下

其中,每个 Cache实例 可以 “根据 Request 作为 key,来存储 Response

Cache: {
    Request1: Response1,
    Request2: Response2
}
1
2
3
4
const VERSION = 'v1';
const offlinePath = './static/m1.jpg';

// 1. 创建 Cache
// 时机:Service Worker 注册完成时
// Api:
//   - window.caches.open()
//   - cache.addAll()
self.addEventListener('install', event => {
    window.caches.open(VERSION).then((cache) => {
        // 指定 Cache实例 要跟踪的 request
        return cache.addAll([
            offlinePath
        ]);
    })
});

// 2. 更新 Cache
// 时机:Service Worker 激活时
// Api:
//   - window.caches.keys()
//   - window.caches.delete()
self.addEventListener('activate', function (event) {
    event.waitUntil(
        // 获取所有Cache对象的key
        window.caches.keys().then(function (cacheNames) {
            return Promise.all(cacheNames.map((cacheName) => {
                // 如果当前版本和缓存版本不一致
                if (cacheName !== VERSION){
                    return window.caches.delete(cacheName);
                }
            }))
        })
    )
});

// 3. 返回 Cache (优先使用网络,失败则使用缓存)
// 时机:捕获到请求
// Api:
//   - window.caches.match()
//   - window.caches.open()
//   - cache.push
self.addEventListener('fetch', event => {
    event.respondWith(
        window.caches.match(event.request)
        .catch(() => fetch(event.request))
        .then(res => {
            window.caches.open(VERSION).then(cache => cache.push(event.request, response))
            return res.clone();
        })
        .catch(() => window.caches.match(offlinePath))
    )
});
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

# 通知推送

通过 Push APINotification API 实现。

# 桌面访问

通过 manifest.json 文件,可以配置 “启动页”、“图标”等信息;也可以实现将 “网页” 添加到主屏幕;

# 参考

更新时间: 11/21/2021, 2:45:24 AM