前端缓存

398 阅读5分钟

缓存方式:

  • Service Worker

  • memory cache

  • disk cache

一、Service Worker

Service Worker充当的是应用程序、浏览器与服务器之间的代理服务器,拦截网络请求并处理响应。旨在创建有效的离线体验,是独立进程。

使用方式如下:

self.addEventListener('install', e => {
  // 当确定要访问某些资源时,提前请求并添加到缓存中。
  // 这个模式叫做“预缓存”
  e.waitUntil(
    caches.open('service-worker-test-precache').then(cache => {
      return cache.addAll(['/static/index.js', '/static/index.css', '/static/mashroom.jpg'])
    })
  )
})

self.addEventListener('fetch', e => {
  // 缓存中能找到就返回,找不到就网络请求,之后再写入缓存并返回。
  // 这个称为 CacheFirst 的缓存策略。
  return e.respondWith(
    caches.open('service-worker-test-precache').then(cache => {
      return cache.match(e.request).then(matchedResponse => {
        return matchedResponse || fetch(e.request).then(fetchedResponse => {
          cache.put(e.request, fetchedResponse.clone())
          return fetchedResponse
        })
      })
    })
  )
})

     Service Worker存储在Application -> Cache Storage中(搭配CacheStorage进行缓存),并且是永久性的,只有通过Cache.delete或者容量超过限制,被浏览器清空。

在Service Wroker没有命中的时候,会查找memory cache、disk cache,如果都没有命中,则会发出fetch请求,一旦经过Service Worker的fetch请求到的资源,都会被标注为from serviceWorker(因为Service Worker在请求到之后使用CacheStorage进行缓存)。

二、memory cache

几乎所有的请求都会进入memory cache,而memory cache会将数据缓存在浏览器的内存中,缓存的资源主要是两部分:

  • preloader:浏览器自己的预加载机制。
  • preload:设置了rel="preload"属性,显式的指定了预加载资源。

注意点:

  • memory cache机制会导致一个页面如果有多个相同的请求时,只会请求一次,避免浪费。

  • 在进行memory cache缓存时,默认的会忽略no-cache、max-age等设置,因为memory cache的生命周期很短,只有一次浏览的机会,所以和no-cache、max-age并不冲突。

  • 因为浏览器内存大小的原因,memory cache作为短期存储。一旦Tab关闭,该页面的memory cache就会失效。或者当该页面的memory cache数据非常多时,前面的数据会自动失效,腾出存储空间。

  • memory cache在匹配缓存时,除了要求URL相同之外,还要求使用类型和域名相同。例如script的url只能用在script上并不能使用在是src上。

三、disk cache

 disk cache的缓存是存在磁盘上的,因此是永久性的,并且对于disk cache缓存的数据是允许跨会话、跨站点的。

disk cache其实就是我们常说的强缓存和弱缓存。

1、强缓存

当客户端发出请求时,会先在缓存数据库中查找请求的资源,如果找到,就直接返回资源,如果没有或者是缓存已过期,就会向服务器发起请求,这就是强缓存

和强缓存有关的header字段有两个:

  • expires:HTTP/1.0字段,值是一个绝对时间,因此服务器和客户端可能存在时间不一致,导致资源失效。

  • cache-control:HTTP/1.1字段,值是一个相对时间,其常用值有:

  • max-age:最大有效时间。

  • muse-revalidate:如果超过了max-age时间,就必须向浏览器发出请求,验证资源是 否有效。

  • no-cahce:字面意思是不缓存,但是实际上还是会缓存的。

  • no-store:不缓存,所有内容都不进行缓存。

  • public:所有的内容都进行缓存,客户端和代理服务器都缓存。

  • private:只能客户端缓存。

cache-control中各字段的优先级:

cache-control优先级高于expires。

强缓存的意义在于:直接减少请求数量。

2、弱缓存

当强缓存失效时,客户端会收到缓存数据库中关于该资源的一个唯一标识,之后浏览器和服务器通信,服务器用这个标识和自己存的标识进行对比,如果一致,则说明没有更新,返回给客户端304,意思是继续使用缓存数据库中的资源。如果不一致,则返回200和新资源。

和弱缓存有关的字段有两对:

  • Last-Modified和Last-Modified-Since

  • Last-Modifed:文件最后的修改时间。

  • Last-Modified-Since:当进行弱缓存时,Last-Modified-Since会被赋予上一次请求的Last-Modified值,也就是文件存储时,记录的Last-Modified值,文件最后一次修改的时间,发送给服务器,服务器和服务器端资源的最后修改时间进行对比,如果一致,返回304,如果不一致,返回200和资源。

  • Etag和If-None-Match

  • Etag:是一个hash值,文件修改时生成。

  • If-None-Match:If-None-Match也是在下次请求时,值为Etag的值,服务器进行对比。

那么为什么会存在两组与弱缓存有关,并且作用一致的字段呢?

因为Last-Modified和Last-Modified-Since时存在问题的:

  • 当资源更新的速度是秒以下的单位时,缓存是不能被使用的,因为缓存的最低时间单位是秒。当资源更新是在秒以下,会出现缓存不是最新资源的情况。
  • 如果文件是服务器动态生成的,那么服务器的资源更新时间一直和缓存数据库中的资源更新时间不一致,缓存没有意义。

Etag的优先级高于Last-Modified的。

弱缓存的意义在于:减小响应体体积。

四、缓存位置对比

  • Service Worker:存储在Cache Storage中, 手动Cache.delete或者超出容量,被浏览器清除。
  • memory cahce:存储在浏览器内存中,过期失效或者内存不够,前面的失效。
  • disk cache:存储在磁盘中,浏览器自动清理机制,删除最老的资源。

五、缓存读取的顺序

  • Service Worker

  • memory cache

  • disk cache

  • 网络请求

六、缓存添加的顺序

  • disk cache
  • memory cache
  • Service Worker

七、启发式缓存

启发式缓存是什么?

启发式缓存时浏览器默认的一种缓存方式,触发的条件是:响应中关于该资源的过期时间max-age和expires都没有

用于确定过期时间的策略是:取Date和Last-Modified差值的10%作为过期时间。

补充:

1、no-cache和must-revalidate搭配使用时,会强迫浏览器每次请求资源时,都向服务器验证资源的有效性。

参考:

developer.mozilla.org/zh-CN/docs/…

juejin.cn/post/684490…