目录
-
为什么加 serviceWorker 缓存
-
加缓存后引发的问题
-
探索强制更新方案
-
实现细节
为什么加 serviceWorker 缓存
serviceWorker 具体用法可参考文章 [serviceWorker 实战]
下面通过几张图的对比来看下加缓存后的具体效果:
深圳请求广州服务器,请求距离近,不加缓存 51ms

深圳请求广州服务器,请求距离近,加缓存后 27ms

深圳请求南京服务器,请求距离远,不加缓存 106ms

深圳请求南京服务器,请求距离远,加缓存后 44ms

用 3G 网络,不加缓存 594ms

用 3G 网络,加缓存后 26ms

对比后发现:
-
不加缓存:html 请求时间,与服务器距离和网速有关,距离越远、网络越差耗时越多,在 50ms~600ms+
-
加缓存:html 请求时间,与服务器距离和网速有无关,稳定在 50ms 以内
结论:
加 serviceWorker 缓存可确保各地用户在网络不好的时候都能快速拿到 html
加缓存后引发的问题
缓存更新策略:
用户进入网页,没有缓存,通过网络请求 html,拿到结果后加入缓存
用户进入网页,有缓存后,先读取缓存的 html 去渲染执行,然后请求新的 html 去更新缓存
通过下面两张图来看下 serviceWorker 具体的更新机制:


存在的问题:
用户需要进 2 次网页才能看到最新的版本功能
探索强制更新方案
如何实现按需更新网页?探索了以下几个方案:
方案1-文件变更
最开始想到的方法:
发版的时候更新 serviceWorker.js 里面的版本号引起文件的变更从而使 serviceWorker 重新安装激活

执行顺序:fetch 请求 html => serviceWorker.register() => 发现变更重新安装激活
结论:方案行不通,html 的请求是最早的,而 serviceWorker 的注册、安装、激活更滞后
方案2-监听更新事件
监听 serviceWorker 的更新事件:
-
当 ServiceWorker installing 属性获取新的服务工作线程时,会触发 updatefound 事件
-
更新 serviceWorker.js 里面的版本号,引发重新安装激活,同时监听 updatefound 事件
-
发现有更新就出提示弹框,用户点击后,强制刷新浏览器更新
结论:方案可行,但是用户体验不好,用户需要手动点击强制刷新
方案3-自动刷页面
偷偷刷新页面:
用户进入页面后,出 loading,自动去刷新 1 次页面
可以前端自己强刷 1 次
内嵌在客户端的可以让客户端容器强刷 1 次
结论:体验不好
方案4-配置文件
在 fetch 里做拦截,比较远程配置:

fetch 拦截时机最早,拦截 html 请求的时候,先去请求1个远程配置文件
然后比较配置文件里的版本号和缓存的版本号,一致走缓存,不一致走网络请求
请求远程配置文件会有一定耗时,耗时过久会抵消缓存产生的效益
结论:方案可行,需要控制住配置文件请求耗时
方案5-URL标识
在 fetch 里做拦截,比较 url:

fetch 拦截时机最早
需要强制刷新的时候,修改访问链接 url 携带的标识字段值
fetch 拦截 html 请求的时候,通过比较 url 标识和缓存标识,一致走缓存,不一致走网络请求
需要具备变更 url 的条件(比如由内嵌容器控制入口和变更)
结论:方案可行,需要 url 具备可变更条件
实现细节
因为项目内嵌在客户端容器里,支持动态拼接修改 url,所以用的是方案5
下面看下具体实现:
实现逻辑
-
网站核心入口是 html,资源类型为 document
-
客户端控制在跳对应网站的时候,url 上拼接 readerVersion=new1
-
serviceWorker fetch 拦截 html,判断缓存的 readerVersion 值和 url 上的值是否一致:
-
一致,走本地缓存,同时更新 serviceWorker 的缓存
-
不一致,走网络请求,请求成功后更新 serviceWorker 的缓存
-
网络请求失败走 serviceWorker 的旧缓存兜底
具体操作
-
前端代码发布上线,此时 readerVersion => ''
-
配置平台修改 readerVersion => new1+
-
上线完毕,看数据大部分用户更新后,可恢复配置 readerVersion => '',也可不恢复
注意事项
-
强制更新客户端 readerVersion=new1 每次需要加 1,new1 => new2
-
fetch 拦截 html 请求需要排除 favicon.ico,服务器会自动请求此图标链接,同样返回 html
