Caching Files With Service Worker
浏览器的缓存是把双刃剑,使用得当的话,可以加快页面的加载速度,减少向服务器的请求次数,进而减少带宽和服务器压力。但是如果使用不得当的话,会造成新功能或者问题修复未能正常生效等问题,用户体验有所下降。常见的缓存方式包括强制缓存和协商缓存,分别用在不同情境下,通过相关的 Headers 控制(感兴趣的可以看之前 HTTP Headers 文章中关于缓存的介绍),也是大多数网站的首要技术选择。那么还有什么其他缓存相关的技术可以使用吗?让我们来关注一下Service Worker
。
快速入门
Service Worker
旨在通过代码精确控制缓存文件和 HTTP 请求,是已经被废弃掉的AppCache
技术的替代方案。Service Worker
有相关的生命周期概念,如下所示:
实际案例
我们来看一下语雀是如何使用Service Worker
来缓存相关内容的。
注册Service Worker
ServiceWorkerContainer
在navigator
下,所以先判断是否支持Service Worker
,不支持的话另做处理
1 | if ('serviceWorker' in navigator) { |
ServiceWorkerGlobalScope
逻辑处理
在 Worker 内不存在全局变量window
取而代之的是self
,这里注册了两个 Scope 变量assets
和resourceBase
,并用importScripts
引入新的逻辑处理
1 | self.assets = [ |
值得注意的是语雀把react
,react-dom
,moment
等不常变更的依赖放在了Service Worker
内,实现了另一种形式的app vendor chunk
处理缓存
主要逻辑如下所示:
1 | self.addEventListener('install', e => { |
这里实现了如下逻辑:
- 在 install 阶段调用
cache.addAll()
将self.assets
内的文件缓存 - 在 active 阶段将新旧的
self.assets
进行对比,并将失效的缓存删除掉:self.assets.includes(t.url) || e.delete(t)
- 注册
fetch
事件监听,如果请求 url 在[self.resourceBase, "https://at.alicdn.com/t/", "https://gw.alipayobjects.com/os/"]
内则从缓存内拿相应的文件
可以看出语雀使用Service Worker
进行缓存,整体的逻辑简单明了,并没有什么复杂高深的内容在里面。
其它应用
我们从语雀处理缓存逻辑时的fetch
事件监听可以看出来,fetch
可以做的不止从缓存内拿相应的文件这么简单,我们可以完全控制整个 fetch 过程。那么可以做的事情就比较多了:
- 缓存一个离线的 html,当检测到无网络时,展示相应的 html 内容,提升用户体验
- 对请求不到资源的情况做错误处理,展示相应的内容
- 将所有图片换成我的支付宝付款码 🙈
总结
Service Worker
做缓存还是挺有用的,相比较Cache-Control
之类的 Headers 来做缓存控制而言,拥有更细粒度的控制过程,并且可以做相应的错误和降级处理。但是依然需要注意缓存带来的时效性问题,否则得不偿失。