深入浏览器缓存机制

深入浏览器缓存机制

我们在开发过程中可能经常看到 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 高,主要取值为:

  • public :所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private :所有内容只有客户端可以缓存,Cache-Control 的默认取值
  • must-revalidate :如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。
  • no-cache :客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store :所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  • max-age=600 :缓存内容将在 600 秒 (任意正整型数字)后失效
使用场景
  • 关闭缓存 Cache-Control: no-store
  • 缓存静态资源:Cache-Control: public, max-age=31536000
  • 重新验证缓存: Cache-Control: no-storemax-age=0

细心的朋友可能发现了,一开始的例子里,第二次请求的时候 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)

协商缓存

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

  • 协商缓存生效,返回 304 和 Not Modified
协商缓存生效
  • 协商缓存失效,返回 200 和请求结果
协商缓存失效

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

  • Last-Modified / If-Modified-Since

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

  • Etag / If-None-Match

下次请求时把上次返回的 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 链接

协商缓存失效过程

浏览器缓存全过程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352