这是我参与8月更文挑战的第22天,活动详情查看: 8月更文挑战
Service Worker生命周期
生命周期回顾
Service Worker生命周期的各个环节如图所示:
通常每一个Service Worker都会依赖于各自独立的执行脚本,在页面需要使用时通过请求相应的执行脚本进行Service Worker的注册;当注册流程启动后,便可在执行脚本中监听install事件来判断安装是否成功,若安装成功则可进行有关离线缓存的处理操作。
但此时的安装成功并不意味着Service Worker已经能够取得页面的控制权,只有进行激活后,Service Worker才能监听页面发出的fetch和push等事件;如果Service Worker的执行脚本发生修改需要进行更新,则更新的流程也会涉及完整的生命周期。上图为Service Worker生命周期所涉及的五个关键状态,之后会一次讨论每个状态中所包含的关键处理操作。
激活
若想要Service Worker获得页面的控制权,跳过安装完成后的等待期,十分简单的方式就是直接刷新浏览器,此时新的Service Worker便会被激活。当然也可以通过调用self.skipWaiting()方法来逐出当前旧的Service Worker,并让新的Service Worker进入activated激活态以获得对页面的控制权。代码示例如下:
self.addEventListener('install', event => {
// 让Service Worker进入激活态
self.skipWaiting()
}
响应缓存
当Service Worker安装成功并进入激活态后,便可以接收页面所发出的fetch事件来进行缓存响应的相关操作。代码示例如下:
self.addEventListener('fetch', event => {
event.respondWith(
// 在缓存中进行验视,看是否能匹配到已有的缓存内容
caches.match(event.request)
.then(response => {
// 如果匹配到已有的缓存内容则直接返回缓存内容
if(response) return response
// 否则重新发起请求
const fetchRequest = event.request.clone()
return fetch(fetchRequest).then(response => {
if(!response || response.status !== 200 || response.type !== 'basic')
return response
}
// 请求成功返回,现将其纳入缓存
const responseToCache = response.clone()
caches.open(cacheName)
.then(cache => cache.put(event.request, responseToCache))
return response
}
)
})
)
})
上述代码的基本流程是这样的:当捕获到一个页面请求后,首先使用caches. match()方法在本地缓存中进行检索匹配;如果检索到则返回缓存中储存的资源内容,否则将调用fetch()方法发起新的网络请求;当接收到请求的响应后,依次判断响应是否有效、状态码是否为200及是否为自身发起的请求,经过判断,如果响应符合预期则将其放入对应请求的缓存中,以方便二次拦截到相同请求时能够快速响应。
更新
当发生Service Worker执行代码的修改时,便需要对浏览器当前的Service Worker进行更新,更新的步骤与初始一个全新Service Worker的生命周期相同。
需要注意的是,应当将缓存管理放在activate事件的回调中进行处理,其原因是当新的Service Worker完成安装并处于等待状态时,此时页面的控制权依然属于旧的Service Worker,如果不等到激活完成就对缓存内容进行清除或修改,则可能导致旧的Service Worker无法从缓存中提取到资源,这会是一个很不好的使用体验。