浏览器缓存

memory cache 和 disk cache 的区别

静态资源文件,可以放在 memory cache 也可以放在 disk cache 里,那么这两个有什么区别呢?

  • memory cache

memory cache 是内存中的缓存,主要包含当前页面中已经下载的资源,比如样式、脚本、图片等等。读取内存中的数据肯定比磁盘块,但是内存缓存的时间段,会随着进程的释放而释放。一旦我们关闭 Tab 页面,内存中的缓存就被释放了。

因为内存有限,并不是所有的资源文件都会放在内存里缓存,它主要用来缓存有 preloader 相关指令的资源,比如。preloader 可以一边解析 js/css 文件,一边网络请求下一个资源。

  • disk cache

disk cache 也就是存在磁盘上的缓存,读取速度比 memory cache 慢点,但是所有的资源文件都可以存在磁盘里。在所有浏览器缓存中,disk cache 覆盖面最大,它会根据 HTTP Header 中的字段判断哪些资源需要缓存,哪些资源已经过期需要重新从服务器端请求。

一般来说,大的文件和使用频率高的文件,会被优先放在磁盘里。

浏览器缓存

从服务器返回的 Http Response Header 中,通常有这几个属性用来标识缓存:

Cache-Control
Expires
ETag / If-None-Match
Last-Modified / If-Modified-Since
其中 Expires 和 Cache-Control 是用来标识强缓存,ETag 和 Last-Modified 用来标识协商缓存。

  • 强缓存
  1. Expries

    1. 强缓存是不会向服务器发送请求,直接从缓存中读取资源

    2. 在 DevTools 的 Network 可以看到该请求的状态码是 200,并且在 Size 那一列里会显示 from memory cache 或者 from disk cache。

    3. 强缓存是通过 Expires 和 Cache-Control 来实现的

    4. Expries 的值是一个 GMT 时间

    这个属性是 http1.0 里的,表示缓存里的文件在这个属性对应时间以后过期。在 HTTP Response 时告诉浏览器在过期时间内可以直接从浏览器缓存里读取数据,无需再次请求
    Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效。

  2. Cache-Control

涉及时间的单位都是 s

Cache-Control 是 HTTP/1.1 的产物,同时有 Expires 和 Cache-Control 的时候,Cache-Control 的优先级要比 Expires 要高,比如 Cache-Control: max-age=300,表示这个请求的返回时间(浏览器会记录该时间)的五分钟之内再次加载该资源,就会从缓存中读取该文件,无需再次请求

1. 表示资源可以被缓存在任何可以被缓存的地方(CDN,中间代理服务器,浏览器等等),可以多用户共享
```
Cache-Control: public
```
2. private 表示资源只可以被浏览器缓存,是私有缓存
```
Cache-Control: private
```

3. no-store表示浏览器需要每次都从服务器拿资源,真正的不缓存数据到本地。

```
Cache-Control: no-store
```

4. no-cache并不是不缓存的意思,而是告诉浏览器要使用缓存文件,但是每次需要跟服务器确认是最新文件以后才能用,一般使用Etag或者Last-Modified字段来控制缓存,看Etag/Last-Modified是否跟服务器匹配,如果匹配就返回304告诉浏览器从缓存里取数据,不匹配就返回200并且重新返回数据。

```
Cache-Control: no-cache
```

5. 表示资源文件缓存的相对时间,这个请求的返回时间(浏览器会记录该时间)的1分钟之内再次加载该资源,就会从缓存中读取该文件,无需再次请求。

```
Cache-Control: max-age=60
```

6. 覆盖max-age,作用一样,只在代理服务器中生效

```
Cache-Control: s-maxage=60
```

7. 表示只有校验缓存里是最新文件才能用缓存里的版本

```
Cache-Control: must-revalidate
```

8. 能容忍的最大过期时间。max-stale指令标示了客户端愿意接收一个已经过期了的响应。如果指定了max-stale的值,则最大容忍时间为对应的秒数。
```
Cache-Control: max-stale=30

```

9.表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。

```
Cache-Control: max-fresh=30
```
  • 协商缓存

协商缓存是指在强缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识来决定是否使用缓存

  1. 缓存还有效,返回 304 和 Not Modified

  2. 缓存失效,返回 200 和请求结果

协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对 Header 来管理的

  1. Last Modified

浏览器第一次访问资源,服务器会 Response Header 里添加 Last-Modified 的值,表示这个资源在服务器的最后修改时间,浏览器在下一次请求这个资源的时候,检测到有 Last-Modified 这个 Header,就会在 Request 中加上 If-Modified-Since,If-Modified-Since 的值就是 Last-Modified 的值。服务器收到这个请求,会对比 If-Modified-Since 和服务器上这个资源的最后修改时间,如果一致就返回 304 和空的响应体,让浏览器直接从缓存里读取;如果 If-Modified-Since 的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和 200。

但是 Last-Modified 有两个弊端:

  1. 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源

  2. 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
    所以在 HTTP/1.1 里有了 ETag 和 If-None-Match。

  3. Etag

Etag 是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag 就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的 Etag 值放到 request header 里的 If-None-Match 里,服务器只需要比较客户端传来的 If-None-Match 跟自己服务器上该资源的 ETag 是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现 ETag 匹配不上,那么直接以常规 GET 200 回包形式将新的资源(当然也包括了新的 ETag)发给客户端;如果 ETag 是一致的,则直接返回 304 知会客户端直接使用本地缓存即可。

ETag 的优先级要高于 Last-Modified。

  • 缓存机制总结

强制缓存优先于协商缓存进行,若强制缓存(Expires 和 Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since 和 Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回 200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回 304,继续使用缓存。

  • 如果浏览器没有设置缓存策略,缓存怎么处理?

缓存时间= (Response Date - Last Modified) % 10

  • 用户行为对浏览器缓存的影响

所谓用户行为对浏览器缓存的影响,指的就是用户在浏览器如何操作时,会触发怎样的缓存策略。主要有 3 种:

打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。

普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。强缓存无效 协商缓存有效

强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容。 强缓存和协商缓存都无效。

  • 解决缓存的版本过旧的问题
  1. 第一种方案就是每次手动给这个文件加个版本号

  2. 第二种方案就是每次对应静态文件里有内容改动的时候,自动加一段hash到静态文件名里

你可能感兴趣的:(浏览器缓存)