讲讲强缓存和协商缓存,F5 和 Ctrl + F5 的区别

讲讲强缓存和协商缓存,F5 和 Ctrl + F5 的区别

本文参考: 深入理解浏览器的缓存机制

PS:本文面向面试题,所以只讲些知识点扫盲,详细分析自行查阅参考链接

基本概念

网上常讲的强缓存和协商缓存(也有叫对比缓存)都是浏览器的缓存策略

需要先明确一点,既然是浏览器的缓存,缓存的数据都是存放于客户端的机子上的,只是根据优先级不同,存储位置不同,还可以进行细分而已,比如 Service Worker,Memory Cache,Disk Cache

那么,强缓存和协商缓存的区别是什么?

就我个人理解,它们的区别其实在于交给谁来做决策,谁来决定是否要使用缓存

强缓存:浏览器在发请求前,会自行去做一些缓存判断工作,自行决定到底要不要发请求,要不要直接使用缓存

协商缓存:浏览器决定不了,必须发个请求给服务端,交由服务端来告知浏览器到底能不能使用缓存

所以,其实,对于强缓存来说,是有可能不用发请求的,体现在浏览器的开发者工具 Network 抓包中就是,size 一栏会明确显示该请求从本地缓存读取

对于协商缓存来说,请求是必须要发的,但服务端判定客户端可以使用缓存时,就不会返回响应体了,体现在响应头中就是 304

相关 header 字段

强缓存、协商缓存都是通过请求头和响应头中的字段来实现的,相关的 header 字段有:Expires,Cache-Control,Last-Modified,If-Modified-Since,ETag,If-None-Match

这些字段,有的是在请求头中使用,有的是在响应头里使用,下面就来讲讲:

  • 请求头
1
2
3
4
5
GET / HTTP/1.1
Host: gitbook.dasu.fun
Cache-Control: max-age=0
If-None-Match: "5e0a2038-4714"
If-Modified-Since: Mon, 30 Dec 2019 16:05:12 GMT
  • 响应头
1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Server: nginx/1.17.5
Date: Tue, 31 Dec 2019 05:48:07 GMT
Last-Modified: Mon, 30 Dec 2019 16:05:12 GMT
Cache-Control: no-cache
Pragma: no-cache
Expires: 0
ETag: "5e0a2038-4714"

expires

1
2
// 响应头
Expires: Thu, 10 Nov 2017 08:45:11 GMT

这是响应头中的字段, http 1.0 就有了,内容表示一个绝对时间,含义也就是缓存的到期时间

这样,当浏览器接收到这样的响应头,它就知道,这份资源的下次请求,只要在过期时间前,就没必要再发起请求,直接使用本地缓存即可

缺点则是,由于是绝对时间,需要依赖于客户端本地时间

Cache-Control

1
2
3
4
// 请求头
Cache-Control: max-age=0
// 响应头
Cache-Control: no-cache

这个是 http 1.1 新增的字段,请求头和响应头中都可以使用

这字段的内容有多个取值,不同取值对应不同缓存行为,客户端和服务端就是通过这字段来进行沟通缓存事宜

这里列举一些取值,更多请查阅 MDN

  • max-age:设置缓存最大有效期,相对时间,单位 s

  • must-revalidate:一旦缓存过期,需要重新向服务端发请求验证

  • no-cache:本地缓存能不能用,交由服务端决定,所以需先发请求给服务端

  • no-store:禁止缓存,不使用缓存,包括内存、磁盘缓存

  • public:代理和客户端都可以缓存

  • private:只有客户端可以缓存,代理不能缓存

这个字段其实就是用来替代 http 1.0 中的 Expires 字段

开头所说的强缓存、协商缓存也是通过这个字段来实现,比如当响应头中带有 Cache-Control: max-age=60,浏览器如果发现在缓存过期前再次请求该资源,就可以直接使用本地缓存了,这就是强缓存

当响应头中带有 Cache-Control: no-cache 时,浏览器就知道,本地的缓存它做不了主,需要它再次向服务端发请求确认是否可以使用本地缓存

Last-Modified & If-Modified-Since

强缓存时,浏览器可以直接自行决定是否使用本地缓存,但协商缓存时,是需要跟服务端进行交互,那么,服务端是如何确认客户端的缓存是否可用的呢?

这就是这两字段的用途了,其中 Last-Modified 是响应头中的字段,服务端返回给客户端资源时,可用携带上该字段,表明这份资源最近一次更新的时间

1
2
3
4
5
// 响应头
Last-Modified: Mon, 30 Dec 2019 16:05:12 GMT

// 请求头
If-Modified-Since: Mon, 30 Dec 2019 16:05:12 GMT

而当浏览器向服务端协商本地缓存是否可用时,就需要把本地缓存的资源中记录的 Last-Modified 值写入请求头的 If-Modified-Since 字段中,服务端接收到后,读取该字段值,就可以去进行验证了,如果客户端可使用缓存,那么就返回 304,否则返回 200 将新资源下发

ETag & If-None-Match

Last-Modified 的协商方式有些局限性,比如当资源的更新是秒级别以内时,或者资源文件确实有改动,但内容没变化时,这些场景,它无法识别出来

所以,引入的 ETag 和 If-None-Match 字段可以用来弥补这些场景

1
2
3
4
5
// 响应头
ETag: "5e0a2038-4714"

// 请求头
If-None-Match: "5e0a2038-4714"

同样,ETag 是响应头中的字段,If-None-Match 是请求头中的字段

服务端可以对资源内容生成 hash 值返回给客户端,一旦资源内容更新,hash 值也就不一样了,客户端的请求中携带了缓存资源的 hash 值,服务端通过比对,就可以确认资源是否有发生变化,就可以告知客户端是否可以直接使用缓存

但相对而言,较耗性能

用户行为

用户有三种行为会涉及到缓存:

  • 新打开 Tab,输入 url,加载网页
  • F5 刷新或者地址栏回车(等同于F5)
  • Ctrl + F5 刷新

当新打开一个 Tab 来加载网页,或者在当前 Tab 输入新的 url 加载新网页时,此时浏览器的内存缓存就没有使用场景了,缓存策略就是根据相关 header 字段来决定是走强缓存还是协商缓存

F5 刷新页面时,如果允许缓存,那么会优先从内存缓存中寻找,再根据 header 字段来判断走强缓存还是协商缓存

Ctrl + F5 也叫强制刷新,此时发给服务端的请求头中不会携带 If-Modified-Since 或 ETag 字段,那么服务端自然只能重新下发资源

知识点

面试时遇到该题,记住这些知识点,然后根据需要,扩展来讲:

  • 强缓存、协商缓存

  • Expires、Cache-Control

  • Cache-Control 的几种取值

    • no-cache
    • max-age
    • no-store
  • Last-Modified & If-Mofidied-Since

  • ETag & If-None-Match

  • F5 & Ctrl + F5

请叫我大苏 wechat
您的支持将鼓励我继续创作!