service worker 实战
service worker 实战
service worker 是现代浏览器的一个新特性,伴随着谷歌的 PWA 概念一起,在近期广泛的出现在大家的视野里。
关于 service worker 的概念和相关的机制,本文不再介绍,大家可以参考 mdn 或自行 google。
本文主要说说我是怎么使用 service worker 的。
做什么?
service worker 故名意思,就是一个提供 service 的 worker。
通常 service worker 扮演着一个 proxy server,而目前因种种原因,我们主要是用它来做一个缓存代理。
最终,通过这个代理来加速我们 web 的加载,甚至是实现我们 web 的离线加载。
怎么做?
要通过 service worker 实现一个缓存代理其实很简单,借助谷歌的 sw-toolbox
这个库,简直可以说是分分钟搞定。以我的一个 h5 应用 为例:
- 首先创建一个 service worker 的脚本文件 sw.js
- 在脚本里引入 sw-toolbox
importScripts('/static/js/lib/sw-toolbox.js');
- 为了 service worker 能尽快应用,分别在 install 事件中调用
self.skipWaiting
,在 activate 事件中调用self.clients.claim
- 针对应用的资源制定合适的缓存策略。
- 最后在页面中注册 service worker,即可。
完整 sw.js
代码如下:
var version = '1.2.0';
self.__uri = function(src) {
return src
};
importScripts(__uri('/static/js/lib/sw-toolbox.js'));
importScripts(__uri('/static/js/lib/sw/monitor.js'));
self.addEventListener('install', function(event) {
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', function(event) {
event.waitUntil(self.clients.claim());
});
toolbox.router.get('/:module/sw', toolbox.networkOnly);
toolbox.router.get('/static/(.*)', toolbox.cacheFirst);
toolbox.router.get('/(.*)', toolbox.networkFirst);
toolbox.router.get('/(.*)', function(request, values, options) {
// 跨域,针对微信添加额外的头
var newRequest = new Request(request, {
headers: {}
});
return toolbox.cacheFirst.apply(this, [newRequest, values, options]);
}, {
origin: /^https?:\/\/at\.alicdn\.com/,
cache: {
name: 'font_1457169990_5499172',
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 7
}
});
页面注册代码如下:
if (navigator.serviceWorker) {
fetch(G.root + '/sw').then(function(res) {
if (res.ok) {
return res.json();
}
throw res.statusText;
}).then(function(resData) {
if (resData.data.on) {
navigator.serviceWorker.register(__uri('/andyunjing/sw.js'));
} else {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
// console.log(registrations.scope)
registrations.forEach(function(registration) {
registration.unregister();
});
});
}
});
}
缓存策略
sw-toolbox 提供了 networkFirst、cacheFirst、fastest、cacheOnly、networkOnly 5种缓存应用策略,相信大家看名字也基本能猜到相应的逻辑了,具体的可查看官方文档了解。
在我的代码里面主要用到了 networkFirst、cacheFirst、networkOnly 这3种。
- 针对
/:module/sw
这个开关接口请求,始终进行网络请求,即 networkOnly - 针对网页的静态资源,如果有缓存,则用缓存的,否则进行请求,即 cacheFirst
- 针对网页的页面,为了获取最新的数据,首先从网络请求,但如果没网的话,也可以用缓存里面的页面,即 networkFirst
其他
在完整的代码里面,除了引入 sw-toolbox
库和配置缓存策略外,还有一些其他的东西,是我参考 https://zhuanlan.zhihu.com/p/25800461
这篇文章的经验,实现的服务器端的开关控制和相关的统计打点。
还有一个就是跨域资源的加载,针对微信去掉 x-qid
的头,避免触发 options 请求。
效果
最终的页面,在首次加载的时候完成 service worker 的注册和启用,在第二次加载完成页面资源的缓存,然后第三次打开小伙伴们就能享受到网页秒开的爽快了!
service worker 作为一个渐进增强的选项,在不改动原有代码的情况下,通过简单的开发就是带来更好的体验,大家值得一试!