深入浏览器缓存机制

2020-05-22  本文已影响0人  爱鸽子的玉米君

深入浏览器缓存机制

我们在开发过程中可能经常看到 200 (memory cache) 200 (disk cache) 304 Not Modified 的状态码,他们分别是什么意思?

这里先有个大体的概念,200 状态码是浏览器的强制缓存机制,304 则是协商缓存机制,这两个缓存机制是通过 HTTP 报文的缓存标识来生效的。

这里我们分析一个普通的 HTTP 请求的请求报文,省略了不影响缓存的消息头

第一次打开 hawtim.github.io/blog ,GET 请求

general
Request URL: https://hawtim.github.io/blog/
Request Method: GET
Status Code: 200
Remote Address: 185.199.111.153:443
Referrer Policy: no-referrer-when-downgrade
request headers
:authority: hawtim.github.io
:method: GET
accept: text/html
# 注意 cache-control 和 pragma
cache-control: no-cache
pragma: no-cache
#============================
response headers
content-encoding: gzip
content-type: text/html; charset=utf-8
date: Thu, 21 May 2020 03:49:51 GMT
# 注意 cache-control etag expires last-modified
cache-control: max-age=600
etag: W/"5ec1ee1c-8ac"
expires: Thu, 21 May 2020 03:51:50 GMT
last-modified: Mon, 18 May 2020 02:08:28 GMT
#=============================================
server: GitHub.com
status: 200 # 现在的状态是 200

第二次 GET 请求 (CTRL + R / Command + R)

general
Request URL: https://hawtim.github.io/blog/
Request Method: GET
Status Code: 304
Remote Address: 185.199.111.153:443
Referrer Policy: no-referrer-when-downgrade
request headers
:authority: hawtim.github.io
:method: GET
accept: text/html
# 注意 cache-control if-modified-since if-none-match
cache-control: max-age=0
if-modified-since: Mon, 18 May 2020 02:08:28 GMT
if-none-match: W/"5ec1ee1c-8ac"
# =================================================
response headers
content-encoding: gzip
content-length: 924
content-type: text/html; charset=utf-8
date: Thu, 21 May 2020 04:13:25 GMT
# 注意 cache-control etag expires last-modified
cache-control: max-age=600
etag: W/"5ec1ee1c-8ac"
expires: Thu, 21 May 2020 04:23:25 GMT
last-modified: Mon, 18 May 2020 02:08:28 GMT
#=============================================
server: GitHub.com
status: 304 # 状态变成 304

看完上面的例子,你可能还对上面注意的消息头,比如
cache-control expires
last-modified if-modified-since
etag if-none-match
有点懵,不知道它们是怎么在浏览器的缓存机制中生效的,没关系,我们下面来分析缓存过程

缓存过程分析

第一次发起 HTTP 请求

从图片可以看出

以上两点就是浏览器缓存机制的关键,确保了每个请求缓存存入与读取。根据是否需要向服务器重新发起 HTTP 请求,我们将缓存过程分为两部分,第一部分是强制缓存,第二部分是协商缓存。

强制缓存

强制缓存就是在浏览器的缓存中查找请求结果,并根据该结果的缓存标识决定是否使用该结果。有以下三种情况:

强缓存失效 强制缓存失效, 进行协商缓存 强制缓存生效

强制缓存相关的请求头

  1. pragma
  2. expires
  3. cache-control
pragma

Pragma 是 HTTP/1.0 中规定的通用首部,它用来向后兼容只支持 HTTP/1.0 协议的缓存服务器,因为 那时候 HTTP/1.1 协议中的 Cache-Control 还没出来

Pragma: no-cacheCache-Control: no-cache 效果一致

expires

Expires 是 HTTP/1.0 中规定的通用首部,它用来指指定资源缓存到期的时间。时间是绝对时间,受客户端时间影响。

cache-control

Cache-Control 是 HTTP/1.1中通用首部,同样用来指指定资源缓存到期的时间,max-age 设置的时间的单位为秒,是相对时间,不受客户端时间影响,优先级比 Expires 高,主要取值为:

使用场景

细心的朋友可能发现了,一开始的例子里,第二次请求的时候 github 就是用了 max-age=0来向服务端校验我的文件是否有更改。

浏览器的缓存的存放位置

缓存存放的位置

以 github 的博客请求为例,状态码是灰色 200 则代表使用了强制缓存,请求对应的 size 值则代表缓存存放的位置,分别为 memory cachedisk cache,那这两者有什么差别,什么情况用 memory 什么情况用 disk?

memory cache (内存缓存)

内存缓存会将编译解析后的文件,直接存入该进程的内存,占据一定的内存资源,方便下次使用时快速读取,但是一旦该进程关闭,则该进程的内存会被清空。

disk cache(硬盘缓存)

硬盘缓存是直接将缓存写入硬盘文件中,读取缓存需要进行 I/O 操作,读取该缓存并解析,速度比内存缓存慢。

使用的实际情况分析

还是以博客的页面为例,没有禁用缓存的情况下,多次刷新页面。就会如出现如 memory cache 和 disk cache 都有的情况。

内存缓存

然后关闭这个标签页,然后重新打开这个页面,则会出现全部都是从 disk cache 的情况。

硬盘缓存

这里有一点要注意的是,虽然 rem.js 是在 memory cache 中获取的,但实际上它也存在 disk cache,只是 memory cache 的优先级更高。

在浏览器中,浏览器会将 js 和 base64 的图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而 css 文件,图片后缀的图片文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)

协商缓存

下面我们来讲讲强制缓存的第二种情况,强制缓存标识失效,走协商缓存的过程。
在强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

协商缓存生效 协商缓存失效

协商缓存的标识在响应报文的 HTTP 头中和请求结果一起返回给浏览器。
控制协商缓存的字段分别有:

下次请求时把上次返回的 Last-Modified 作为 If-Modified-Since 的值,服务端发现请求头中有 If-Modified-Since 字段,服务端将该值与资源的最后修改时间比对,大于则重新返回资源,否则返回 304 代表 资源无更新,可继续使用缓存。

下次请求时把上次返回的 Etag 作为 If-None-Match 的值,服务端将 If-None-Match 的值与资源的 Etag 比对,一致则返回 304,代表资源无更新,继续使用缓存文件,不一致则重新返回资源文件,状态码 200

注 Etag / If-None-Match 优先级高于 Last-Modified / If-Modified-Since,同时存在则只有前者生效。

总结

综合上面的分析,现在看文章前面的第一次请求和第二次请求的变化,应该就能很好理解了。

在浏览器的缓存机制中,强制缓存优先于协商缓存。强制缓存的标识(Expires 和 Cache-Control),如果生效则使用缓存,如果不生效则发起请求进行协商缓存的标识验证。(Last-Modified / If-Modified-Since和 Etag / If-None-Match),若协商缓存失效,则重新获取请求结果,再传入浏览器缓存中;生效的话,则继续使用缓存。

浏览器缓存全过程

参考文章

相关 UML 链接

协商缓存失效过程

浏览器缓存全过程

上一篇 下一篇

猜你喜欢

热点阅读