乐趣区

关于http缓存: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 年到当初依然在探讨着这个事件,看来短时间这个问题是得不到解决了。

一些正在探讨的话题:

  1. safari fetches from memory cache instead of Service worker
  2. Difference between disk and memory cache
  3. Advanced Questions About Service Worker
  4. allow service worker produced resources to be marked as “cachable”

咱们真的没有方法么?

要是咱们遇到业务场景,的确对 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 岂不是减慢了站点的加载速度?

对照试验

为了钻研这个问题,我做了一组对照试验。
试验只在 Chrome 进行,chrome devtool 为每个资源提供工夫。所有加载资源的信息都能够作为 HAR 文件下载下来,而后编写本地脚本进行信息提取和剖析。

试验条件

  1. 同一个环境:Chrome97 / MacOS 10.14 / Wifi
  2. 同一张图片的屡次并发加载:

    1. 3 张 133KB 图片 10 次试验
    2. 10 张 133KB 图片 10 次试验
    3. 100 张 133KB 图片 10 次试验
  3. 察看两个性能:

    1. DiskCache 缓存性能体现
    2. ServiceWorker 缓存速度体现

试验一:3 张 133KB 图片并发

首先是并发申请 3 张图片进行 10 次试验,取均匀数据,而后别离察看 DiskCache、ServiceWorker Cache 的性能体现。

察看:

  1. DiskCache:咱们发现下载操作并没有花太多工夫,然而资源在期待排队。
  2. ServiceWorker Cache:更多耗时在下载。

论断:但尽管如此,这种状况下,DiskCache 仍然是比 ServiceWorker Cache 更快。

试验二:3 张 133KB 图片 10 次试验

当我把并发图片减少到 10 张,这种状况可能会更加靠近于理论状况,站点中可能会领有更多的不同的资源(JS 文件、字体、款式、图像等),因为某些网站可能会在一个页面存在超过 10 个资源。

察看:

  1. DiskCache:从第二个资源开始排队工夫仍然很长,然而下载工夫是根本不变的。
  2. ServiceWorker Cache:排队并不是问题,但期待是。

论断:这种状况下,DiskCache 会略逊于 ServiceWorker Cache。

试验三:3 张 133KB 图片 100 次试验

当我把并发图片减少到 100 张,这种状况简直是不实在的状况,然而我好奇为什么 DiskCache 为什么在第一次试验中比 ServiceWorker Cache 更快。

察看:

  1. DiskCache:排队仍然是问题,且随着并发数成线性回升。咱们甚至能看到浏览器是如何加载图片的,一次并发大略 6 张图片。
  2. ServiceWorker Cache:尽管等待时间随着并发数回升,然而是平缓的。

论断: 大并发下 ServiceWorker Cache 比 DiskCache 更快。

那 DiskCache 和 ServiceWorker 怎么抉择?
小孩子才做抉择,小孩儿都要

因为 ServiceWorker 的优先级在 DiskCache 之上,咱们能够在 ServiceWorker 进行 「资源竞速」,同一时间申请 ServiceWorker Cache 和 DiskCache,哪个先返回就把资源返回上一层。代码可能是这样的:

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)
      ])
    })(););
});

试验四:资源竞速之后,并发申请 3 张图片、10 张图片 和 100 张图片

当咱们进行资源竞速之后,这种状况下,无论是并发大量资源还是大量资源,都能达到最快的级别。


总结

本篇咱们搞懂了 ServiceCache、MemoryCache、DiskCache 的优先级。
而后深刻比照了 ServiceWorker Cache 和 DiskCache 的性能体现。
在大量资源并发的时候,DiskCache 更快,在大量资源并发的时候,ServiceWorker 更快。
最初通过「资源竞速」的形式来兼顾两种状况。
然而,在某些时候,咱们比拟 ServiceWorker 和 HTTP 缓存有点不偏心。
ServiceWorker 的用处会更加宽泛,它提供了更细力度的缓存管制、使离线化利用得以实现、并且比照主线程,他可能应用更多的 CacheAPI 容量。

退出移动版