Service Worker 作用域
Service Worker 默认不能控制其上一级目录, Service-Worker-Allowed 指定的目录可以设置最大的 scope(超过它的上一级的限制)。
作用域污染
当同一个页面被多个 Service Worker 所控制,会导致一些问题。
在注册之前,注销所有的 Service Worker,防止 Service Worker 的作用域污染
navigator.serviceWorker.getRegistrations().then(function (regs) {
for (let reg of regs) {
reg.unregister();
}
});
MPA 注册一个 Service Worker
- 不会造成 Service Worker 污染。
- 统一处理整站的离线缓存策略,降低维护成本。
- Service Worker 需要统一管理,增加了项目开发的耦合性。
MPA 注册多个 Service Worker
- Service Worker 维护上和页面关联,增加了灵活性。
- 维护成本增加,风险相对会更大。
- 可能造成 Servcie Worker 污染。
- 不能统一的处理整站的离线缓存方案。
Service Worker 触发更新
- 浏览器每 24 小时更新一次 Service Worker。(浏览器默认行为)
- 注册新的 Service Worker,带上版本号,如:/sw.js?v=2018071920
- 手动更新 registration.update()
- 逐字节对比新的 sw 文件和旧的 sw 文件,有区别时才更新。
Service Worker 更新过程
- 开始更新前,老的 SW 会总是激活。
- 更新后的 SW 会和老的 SW 共同存在,新的 SW 进入 install 生命周期。
- 如果新的 SW 没有 install 成功,它将被废弃,老的 SW 继续保持激活状态。
- 一旦新的 SW 安装成功,它会进入 wait 状态直到老的 SW 不控制任何 clients(页面标签)。
- self.skipwaiting() 可以跳过等待,让新 SW 安装成功后立即激活。
Service Worker 更新遇到的问题
- SW 更新完成后,缓存在更新的过程中已经更新为最新的。
- 页面静态资源在 SW 更新完成之前已经加载完成,所以还是老的。
- JavaScript 可能有的接口在上线的时候有变动。
- 如果请求接口是 Network Only 缓存策略的话会报错。
Service Worker 更新后通知用户
self.addEventListener('activate', function (event) {
// 进入 activate 生命周期,说明新的 SW 已经注册成功。
let cacheName = 'a_cache_name';
event.waitUntil(
caches.open(cacheName)
.then(function(cache){ // 进行老缓存的清除,代码省略
.then(function(cache){
// 这里可以判断如果 cache 里本来就没有内容,表示第一次安装,就不用通知用户了。
return self.clients.matchAll()
.then(function (clients){
if (clients && clients.length) {
clients.forEach(function(client){
// 给每个已经打开的标签都 postMessage
clients.postMessage("sw.update")
})
}
})
})
})
)
});
在检测到页面的 SW 更新时,我们可以:
- 直接 reload 页面(不建议)
- 提醒用户更新
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('message', function(e) {
if (e.data === 'sw.update') {
// 以刷新当前页面为例
// 刷新后 sw 为最新的 sw,页面内容也为最新的。
window.location.reload()
}
})
}
当 SW 文件被缓存,怎么使它更新?
后端设置 Cache-Control: no-cache;
前端更新的策略
Service Worker 兜底方案
Q:如果 Service Worker 在运行过程中出现了问题怎么办?
A:需要找个能快速上线的开关 JavaScript 文件。 https://yourhost.com/switch.js