强缓存和弱缓存

312 阅读8分钟

​ 缓存是我们平时开发中时常会碰到的概念,某些场景下,它会带来一些问题,而在一些场景下,它能方便我们优化项目的。

1. 缓存的意义

在前端方面,缓存其实就是一个资源副本,是对服务器请求返回资源的一种处理,我们要区别开它和本地存储(cookie,localstorage,session)的区别,本地存储更多是数据记录,存储量较小,为了本地操作方便。而缓存更多是为了减少资源请求,多用于存储文件,存储量相对较大。 ​ 缓存最根本的作用就是减少没必要请求,减小服务器的压力,也加快了响应速度,进而优化用户体验,减少损耗。 缓存主要分为 ```Memory cache

```service worker(即cachestorage,pwa的应用

,```HTTPcache

```pushcache

, 本文主要讲的就是httpcache,包括其中的强弱缓存

2. HTTP cache

http cache都需要和后端配合还有运维才能实现,强缓存优先级高于弱缓存。 httpcache主要分为强缓存和弱缓存(协商缓存):

  1. **强缓存:**直接从本地副本比对读取,不去请求服务器,返回的状态码是 200。
  2. **弱缓存:**会去服务器比对,若没改变才直接读取本地缓存,返回的状态码是 304。## 2.1 强缓存 强缓存主要包括 expires 和 cache-control。

2.1.1 expires

expires 是 HTTP1.0 中定义的缓存字段。当我们请求一个资源,服务器返回时,可以在 Response Headers 中增加 expires 字段表示资源的过期时间。 image.png

是一个时间格式,表示最大缓存可用时间,当客户端再次请求该资源的时候,会把客户端时间与该时间戳进行对比,如果大于该时间戳则已过期,否则直接使用该缓存资源。 缺点:发送请求时是使用的客户端时间去对比。一是客户端和服务端时间可能快慢不一致,另一方面是客户端的时间是可以自行修改的(比如浏览器是跟随系统时间的,修改系统时间会影响到),所以不一定满足预期。

2.1.2 cache-control

image.png

由于expire的问题,HTTP1.1 新增了 cache-control 字段来解决该问题,所以当 cache-control 和 expires 都存在时,cache-control 优先级更高。cache-control 主要有 max-age 和 s-maxage、public 和 private、no-cache 和 no-store 等值。

  • max-age: cache-control 的主要字段,它是一个数字,表示资源过了多少秒之后变为无效。当客户端请求资源的时候,发现该资源还在有效时间内则使用该缓存,它不依赖客户端时间,使用的是服务端的时间作为判断的依据。在浏览器中起作用,可以通过设置 max-age 为 0 表示立马过期来向服务器请求资源。
  • **s-maxage:**基本同max-age,优先级高于max-age,在代理服务器中,只有 s-maxage 起作用。 max-age不起作用
  • **public&private:**public 表示该资源可以被所有客户端和代理服务器缓存,而 private 表示该资源仅能客户端缓存。默认值是 private,当设置了 s-maxage 的时候表示允许代理服务器缓存,相当于 public。
  • no-cache: 表示的是不直接询问浏览器缓存情况,而是去向服务器验证当前资源是否更新(即弱缓存)
  • **no-store:**完全不使用缓存策略,不缓存请求或响应的任何内容,直接向服务器请求最新。由于它和 no-cache 完全不考虑缓存情况而是直接与服务器交互,所以当 no-cache 和 no-store 存在时会直接忽略 max-age 等### 2.1.3 pragma 它的值有 no-cache 和 no-store 基本含义也同cache-control, 但是优先级高于它,所以总的来说,强缓存优先级是 pragma -> cache-control -> expires ​

2.2 弱缓存

上面的 expires 和 cache-control 都会访问本地缓存直接验证看是否过期,如果没过期直接使用本地缓存,并返回 200。但如果设置了 no-cache 和 no-store 则本地缓存会被忽略,会去请求服务器验证资源是否更新,如果没更新才继续使用本地缓存,此时返回的是 304,这就是弱缓存。弱缓存主要包括 last-modified 和 etag。 ​

2.2.1 last-modified

last-modified 记录资源最后修改的时间(服务器时间)。启用后,请求资源之后的响应头会增加一个 last-modified 字段,如下: image.png

当我们请求资源的时候,我们请求头中带上if-modified-since如下: image.png

值是之前返回的 last-modified 的值,如:if-modified-since:Fri, 24 Dec 2021 03:50:28 GMT。服务端会对比该字段和资源的最后修改时间,若一致则证明没有被修改,告知浏览器可直接使用缓存并返回 304;若不一致则直接返回修改后的资源,并修改 last-modified 为新的值。 last-modified 有以下两个缺点:

  • 只要编辑了,不管内容是否真的有改变,都会以这最后修改的时间作为判断依据,当成新资源返回,从而导致了没必要的请求响应,而这正是缓存本来的作用即避免没必要的请求。
  • 时间的精确度只能到秒,如果在一秒内的修改是检测不到更新的,仍会告知浏览器使用旧的缓存。​

2.2.2 etag

为了解决 last-modified 上述问题,有了 etag。 etag 会基于资源的内容编码生成一串唯一的标识字符串,只要内容不同,就会生成不同的 etag。启用 etag 之后,请求资源后的响应返回会增加一个 etag 字段,如下: image.png

我们请求的时候,请求头会带上if-none-match字段,如下 image.png

返回的是之前返回的 etag值,如:if-none-match:"W/"61c54384-f82f1"。服务端会根据该资源当前的内容生成对应的标识字符串和该字段进行对比,若一致则代表未改变可直接使用本地缓存并返回 304;若不一致则返回新的资源和状态码200,并修改返回的 etag 字段为新的值。 可以看出 etag 比 last-modified 更加精准地感知了变化,所以 etag 优先级也更高。不过从上面也可以看出 etag 存在的问题,就是每次生成标识字符串会增加服务器的开销。所以要如何使用 last-modified 和 etag 还需要根据具体需求进行权衡。

3. 缓存(访问)刷新分析

接下来我们来实际分析下缓存的场景和机制。 我们将访问和刷新分为以下三种情况:

  • 标签进入、输入url回车进入
  • 按刷新按钮、F5 刷新、网页右键“重新加载”
  • ctrl + F5 强制刷新假设当前有这么一个 index 页面,返回的响应信息如下:
cache-control: max-age=72000
expires: Tue, 20 Nov 2018 20:41:14 GMT
last-modified: Tue, 20 Nov 2018 00:41:14 GMT

3.1 标签进入、输入url回车进入

这种情况下会根据实际设计的缓存策略去判断。

  1. 由于该例没有设置 no-cache 和 no-store,所以默认先走强缓存路线。根据 cache-control (expires 优先级低)判断缓存是否过期,若没有过期则此时返回 200(from cache)。
  2. 若本地缓存已经过期再走弱缓存路线,根据之前的 last-modified 值去与服务器比对,若这个时间之后没有改过则去读取本地缓存,返回 304(not modified)。
  3. 否则返回新的资源,状态码 200(ok),并更新返回响应的 last-modified 值。## 3.2 按刷新按钮(正常刷新) 这种情况下,实际是浏览器将 cache-control 的 max-age 直接设置成了 0,让缓存立即过期,直接走弱缓存路线。发送的请求头如下:
cache-control: no-cache
pragma: no-cache

3.3 强刷

强制刷新的情况下,浏览器会强行设置 no-cache,强制获取最新的资源,就连 if-modified-since 等其他缓存协议字段都会被吃掉。此时发送的请求头如下:

cache-control: no-cache
pragma: no-cache

4 缓存优先级

查看是否有缓存阶段也就是强缓存阶段 image.png

接下来看下各种缓存下,network的表现。强缓存-

image.png

这里要看下from memory cache和from disk cache区别,都是强缓存的返回 但是memorycache是从内存中读取,diskcache是从磁盘中获取,这是浏览器的策略,一般css都会在磁盘中,而js是需要执行的,如果放磁盘,则执行还需要拿到内存中再执行,io开销大,所以一般都是memorycache。 这都是基于chrome来解释的,firefox没有这两个策略。

弱缓存(etag) image.png

参考:强缓存和协商缓存