最近闲来无事,把网站升级了,更加像原生APP

记录下升级过程,免得以后忘了

PWA是什么?可以去网上搜一下,but中文文章少一点

用Google 封装的 好的 workbox 来升级

注册Service Worker

在html页面注册 SW

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<script>
if ('serviceWorker' in navigator) {
        navigator.serviceWorker.getRegistrations()
            .then(regs => {
                for (let reg of regs) {
                    // 注销掉不是当前作用域的所有的 Service Worker
                     if (reg.scope !== 'http://localhost:8000/') {
                        reg.unregister()
                    }
                }
                // 注销掉污染 Service Worker 之后再重新注册自己作用域的 Service Worker
                window.addEventListener('load', () => {
                    navigator.serviceWorker.register('./sw.js').then(function (e) { console.log("支持sw:", e.scope) })
                })
            })
    }
</script>

生成SW.js文件

google cdn

1
2
importScripts("  'https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-sw.js'
");

国内建议不使用Google

jsdelivr cdn

1
importScripts("https://fastly.jsdelivr.net/npm/[email protected]/build/workbox-sw.min.js");

SW.js 编写

配置相关

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
workbox.setConfig(
    {
        // 是否开启debug
        debug: false
    }
);

let cacheSuffixVersion = '-220806'; // 缓存版本号
const maxEntries = 100; // 最大条目数

workbox.core.setCacheNameDetails({
    prefix: 'zsan', // 前缀
    suffix: cacheSuffixVersion, // 后缀
});

//检测是否安装成功
if (workbox) {
    console.log("Yay! Workbox is loaded 🎉")
} else {
    console.log("Boo! Workbox didn't load 😬")
}

预缓存文件

1
2
3
4
5
6
7
8
let cacheFiles = [
    {
        url: "/index.html",
        revision: cacheSuffixVersion
    }
];

workbox.precaching.precacheAndRoute(cacheFiles);

workbox策略

cdn策略

对于一些cdn的使用,使用CacheFirst 缓存优先策略,此策略将首先检查缓存中是否有响应,如果有响应,则使用该策略。如果请求不在缓存中,则将使用网络,并将任何有效响应添加到缓存中,然后再传递给浏览器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
workbox.routing.registerRoute(
    new RegExp('^https://fastly\\.jsdelivr\\.net'),
    new workbox.strategies.CacheFirst({
        cacheName: 'static-immutable' + cacheSuffixVersion,
        fetchOptions: {
            mode: 'cors',
            credentials: 'omit',
        },
        plugins: [
            new workbox.expiration.ExpirationPlugin({
                maxAgeSeconds: 30 * 24 * 60 * 60,
                purgeOnQuotaError: true,
            }),
        ],
    })
);

字体文件

对于字体文件,基本上确定好就不会改动了,这里使用StaleWhileRevalidate,此策略将对网络请求使用缓存来响应(如果有),并在后台更新缓存。如果未缓存,它将等待网络响应并使用它。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
workbox.routing.registerRoute(
    // 匹配 woff 字体
    new RegExp('.*.woff'),
    new workbox.strategies.StaleWhileRevalidate({
        // cache storage 名称和版本号
        cacheName: 'font-cache' + cacheSuffixVersion,
        plugins: [
            // 使用 expiration 插件实现缓存条目数目和时间控制
            new workbox.expiration.ExpirationPlugin({
                // 最大保存项目
                maxEntries,
                // 缓存 30 天
                maxAgeSeconds: 30 * 24 * 60 * 60,
            }),
            // 使用 cacheableResponse 插件缓存状态码为 0 的请求
            new workbox.cacheableResponse.CacheableResponsePlugin({
                statuses: [0, 200],
            }),
        ],
    })
);

API资源

API数据的请求使用NetworkFirst 策略,此策略将尝试首先从网络获得响应。如果收到响应,它将把它传递给浏览器,并将其保存到缓存中。如果网络请求失败,将使用最后一个缓存的响应。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
workbox.routing.registerRoute(
    // 匹配 leancloudapi.xsbb.ml
    new RegExp('^https://(?:blog\\.zsan\\.ml)'),
    new workbox.strategies.NetworkFirst({
        // cache storage 名称和版本号
        cacheName: 'blog-api-cache' + cacheSuffixVersion,
        fetchOptions: {
            mode: 'cors',
            credentials: 'omit',
        },
        networkTimeoutSeconds: 3,
    })
);

Google-Analytics

官方有Google Analytics 离线统计插件,但是英文的,相关资料也少,折腾了好一阵子

1、简单点,一句代码就行,注意用这个时候,head里的代码需要是官方获取的最新的

1
workbox.googleAnalytics.initialize()

2、带离线统计

需要去Google analytics 添加自定义指标和自定义维度

1
2
3
4
5
6
7
8
9
workbox.googleAnalytics.initialize({
    parameterOverrides: {
        cd1: 'offline',
    },
    hitFilter: (params) => {
        const queueTimeInSeconds = Math.round(params.get('qt') / 1000);
        params.set('cm1', queueTimeInSeconds);
    },
});

其他后缀匹配

保证体验良好

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
workbox.routing.registerRoute(
    new RegExp('.*.(?:png|jpg|jpeg|svg|gif|webp|ico)'),
    new workbox.strategies.StaleWhileRevalidate()
);

workbox.routing.registerRoute(
    new RegExp('.*.(css|js)'),
    new workbox.strategies.StaleWhileRevalidate()
);
// 默认
workbox.routing.setDefaultHandler(
    new workbox.strategies.NetworkFirst({
        networkTimeoutSeconds: 3,
    })
);

改造完成,尽情享用🎉

参考资料

初识 Service Worker —— 使用 Workbox 快速开发 Service Worker - 宝硕博客

PWA来了,你准备好了么? - PWA学习手册

HOME · PWA 应用实战

Service worker overview - Chrome Developers