浅析ServiceWorker缓存与HTTP缓存对比了解

  虽然 ServiceWorker 和 PWA 正在成为现代 Web 应用程序的标准,但浏览器资源缓存变得比以往任何时候都复杂。本文涵盖了浏览器缓存的重点内容,具体包括:

  1. ServiceWorker 缓存与 HTTP 缓存的优先级?
  2. 主流浏览器实现的 MemoryCache 和 DiskCache 在哪一层?
  3. MemoryCache、DiskCache、ServiceWorker 缓存哪个速度更快?

一、缓存流程概述

  我们先来看标准定义的资源请求遵循的顺序:

1、ServiceWorker 缓存:ServiceWorker 检查资源是否存在其缓存中,并根据其编程的缓存策略决定是否返回资源。这个操作不会自动发生,需要在注册的 ServiceWorker 中定义 fetch 事件去拦截并处理网络请求,这样才能命中 ServiceWorker 缓存而不是网络或者 HTTP 缓存。

2、HTTP 缓存:这里就是我们常常说的「强缓存」和「协商缓存」,如果 HTTP 缓存未过期的话,浏览器就会使用 HTTP 缓存的资源。

3、服务器端:如果 ServiceWorker 缓存或者 HTTP 缓存中未找到任何资源,则浏览器会向网络请求资源。这里就会涉及到 CDN 服务或者源服务的工作了。

  这是标准定义的资源请求流程,但是有追求的浏览器还会在 ServiceWorker 上面加一层 「内存缓存层」 ,以 Chrome 为例,我们请求一个资源,除去网络,会有三种浏览器缓存返回:

  那么 MemoryCache 和 DiskCache 与 ServiceWorker Cache 的优先级是怎么样的呢?下面我们讲讲三者的区别。

二、MemoryCache、DiskCache 在缓存流程的哪一层

  我们以 Chrome 为例,MemoryCache 作为第一公民,位于 ServiceWorker 之上。也就是命中了 MemoryCache,就不会触发 ServiceWorker 的 fetch 事件。而 DiskCache 则位于原来的 HTTP 缓存层:

  MemoryCache 的存在会导致一个问题: ServiceWorker 并不总是对资源有着控制权。 这会另我们本来期望的情况会变得复杂且不可预知。可惜的是 MemoryCache 并不在 W3C 的标准中,W3C 从 2016 年到现在仍然在讨论着这个事情,看来短时间这个问题是得不到解决了。

  我们真的没有办法么?要是我们遇到业务场景,确实对 ServiceWorker 资源控制权有很强的的要求,我们还是可以做点事情的。

  MemoryCache 是受控于 「强缓存」 的,这意味着我们可以在 ServiceWorker 拦截资源的响应,并设置资源响应头来使资源从 MemoryCache 失效:

cache-control: max-age=0
self.addEventListener("fetch", (event) => {
  event.respondWith(
    (async function () {
      // 从 HTTP 缓存或者网络获取资源
      const res =  fetch(event.request);
      // 因为 Response 是一个流,只能用一次,所以这里要 clone 一下。
      const newRes = res.clone();
      // 改写资源响应头
    return new Response(res.body, { ...newRes, headers: {
        'cache-control': 'max-age=0'
      }});
    })();
  );
});

  需要注意的是,这种方法是以牺牲少量加载性能为前提的。这取决于我们实际场景中是性能优先,还是离线优先,或者其他什么情况优先。

三、MemoryCache、DiskCache、ServiceWorker 缓存哪个速度更快?

  我们再看一下同一个资源三种缓存的加载速度和优先级:

(1)加载速度:MemoryCache > DiskCache > ServiceWorker

(2)优先级:MemoryCache > ServiceWorker> DiskCache

  MemoryCache 优先级在 ServiceWorker 前面,这个没问题。

  但是速度更慢的 ServiceWorker 优先级比速度更快的 DiskCache 更高?那盘下来,ServiceWorker 岂不是减慢了站点的加载速度?

  实验对比:详见原文,这里记录下结论

1、在少量资源并发的时候,DiskCache 更快

2、在大量资源并发的时候,ServiceWorker 更快

3、那 DiskCache 和 ServiceWorker 怎么选择? 都要

  由于 ServiceWorker 的优先级在 DiskCache 之上,我们可以在 ServiceWorker 进行 「资源竞速」,同一时间请求 ServiceWorker Cache 和 DiskCache,哪个先返回就把资源返回上一层。代码可能是这样的:使用 Promise.race()

self.addEventListener("fetch", (event) => {
  event.respondWith(
    (async function () {
      const res = Promise.race([
        // 请求 ServiceWorker Cache
        cache.open(CACHE_NAME).then(cache => cache.match(event.request)),
        // 请求 DiskCache 或者网络资源
        fetch(event.request)
      ])
    })();
  );
});

  总结:

  本篇我们搞懂了 ServiceCache、MemoryCache、DiskCache 的优先级,然后深入对比了 ServiceWorker Cache 和 DiskCache 的性能表现:在少量资源并发的时候,DiskCache 更快,在大量资源并发的时候,ServiceWorker 更快。最后通过「资源竞速」的方式来兼顾两种情况。

学习链接:https://juejin.cn/post/7088741970696208414
posted @ 2020-11-23 17:18  古兰精  阅读(659)  评论(0编辑  收藏  举报