缓存技术之Service Worker缓存-生命周期-激活、响应缓存、更新

172 阅读3分钟

这是我参与8月更文挑战的第22天,活动详情查看: 8月更文挑战

Service Worker生命周期

生命周期回顾

Service Worker生命周期的各个环节如图所示:

未命名文件 (1).png

通常每一个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无法从缓存中提取到资源,这会是一个很不好的使用体验。