浏览器在HTTP协议下的强缓存与协商缓存

起因

最近在写课设使用浏览器调试页面的时候,经常发现会出现"Request content was evicted from inspector cache"(请求已从缓存中逐出)。这让我调试很恼火,因为我看不到响应数据。

上网查了一下,原因可能是base64的头像数据太大,因此浏览器无法展示。这不禁引起了我的兴趣,我想到了几个问题

  • 如何找到浏览器的缓存位置
  • 浏览器的缓存机制是怎样的

如何找到Google浏览器的缓存位置

  1. 在浏览器中输入chrome://version/,获取浏览器信息

  2. 找到个人信息路径,打开我的电脑输入这个路径

3.打开后便可看到浏览器存放缓存的位置。

浏览器的缓存机制是怎么样的

为了更好的描述浏览器的缓存机制,我们可以举一个现实生活中的例子。生鲜超市、冰箱,你。今天突发奇想,非常想吃牛肉火锅,打开冰箱一看发现没有,但是我又不想出门,因此,我打了个电话给生鲜超市,让他给我送一斤鲜牛肉来。坐在家里等了一会,就送到了。你发现包装写着 保质期:2天 可以冷冻保存。然后你将其放进了冰箱里面。

翻译一下就是:"你"也即"浏览器","冰箱"也即"缓存","生鲜超市"也即"服务器","想吃牛肉"也即"发送一条请求数据请求"

  • 浏览器想获取某条数据,先从缓存找没找到
  • 想该数据存在的服务器发送一条请求
  • 服务器返回该请求,并且标识该数据的有效期,在后端的响应头中加上该字段Cache-Control:max-age=30表明该数据的存活期是30s钟。
  • 浏览器将该数据放入缓存

强缓存

上面的使用Cache-Control:max-age=30的例子即为强缓存。当浏览器想要去获取数据的时候,先判断数据是否已经到达Cache-Control所约定的时间,如果未到达过期时间,那么浏览器就会从本地缓存中查询此次数据。

需要注意的一点是,这里的 max-age 是"生存时间"("保质期" 类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在网络中传输过程中所有节点所停留的时间。
例如,服务器设定"max-age=10",但因为网络质量很糟糕,浏览器收到响应报文时已经过去了 4 秒时间,那么这个资源在浏览器最多能够再存活 6 秒钟,之后就会被认为是过期数据。

"max-age"是 HTTP 强缓存最常用的属性,此外在响应报文中还可以使用其他的属性来更精确地让浏览器知道该如何使用缓存:

  • no_store:不允许浏览器缓存,用于某些变化非常频繁的数据,例如像秒杀、抢券页面。
  • no_cache:实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本。
  • must-revalidate:意思是如果缓存不过期就可以继续使用,但是过期了就必须去服务器验证。

我们继续拿买牛肉来举例说明一下:

  • no_store:买来的牛肉不允许放进冰箱,要么立刻吃,要么立刻扔掉。
  • no_cache:可以放进冰箱,但吃之前必须问超市有没有更新鲜的,有就吃超市里的。
  • must-revalidate:可以放进冰箱,保鲜期内可以吃,过期了就要问超市让不让吃。

下面是强缓存的流程图,我想你看了这个图应该很清晰了。

为什么Ctrl + F5可以强制刷新页面拿到最新的数据

因为会在请求头中加上了Cache-Control:max-age=0,表明浏览器要最新的数据。

相同的,在使用浏览器的"回退" "前进",你会发现此时浏览器的请求都是200 (from disk),

协商缓存

浏览器用"Cache-Control"做缓存控制只能是刷新数据,不能很好地利用缓存数据,又因为缓存会失效,使用前还必须要去服务器验证是否是最新版。

那么该怎么做呢?下面则是协商缓存。

在首次向服务器发送请求的时候,服务器会返回一个"Last-modified"和"ETag",一个是"最近更新时间",一个是资源唯一标识。在该数据缓存过期之后,浏览器将会向服务器发送一个请求,不过这个请求会带上上面两个字段,服务器则会进行比对两个字段,如果发现最近更新时间相同,则本次请求不会再有数据传输,告诉浏览器直接从本地缓存读取即可。状态码会被改成304

"Last-modified"

"Last-modified"很好理解,就是文件的最后修改时间。

"ETag"

ETag 是"实体标签"(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。

  • 一个是,如果修改的次数的频率在秒(s)级,即某个文件在一秒内被修改了很多次,因此无法区分版本的最新性。
  • 还有一个则是,一个文件更新了之后,依旧是同样的内容,没有变化,但是利用修改时间,就会产生误解导致重新传输数据浪费带宽。

使用 ETag 就可以准确地识别资源的更改情况,让浏览器能够更加有效地利用缓存。

不过,ETag 还有"强""弱"之分。

强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个"W/"标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。

另外,关于条件请求中的其他三个字段是"If-Unmodified-Since""If-Match"和"If-Range",如果感兴趣,可以自己猜一猜有什么用再去搜一搜。

总结

本文讲解了 HTTP 的强缓存和协商缓存,利用好它们可以减少响应时间、节约网络流量。

强缓存指的是利用Cache-Control中的请求TTL,如果未过期,依旧可以从缓存获取。

而对于协商缓存,指的是在发现浏览器请求过期后,先向服务器发送一个协商请求,拿上Etag与服务器的该Etag判断,是否数据未更改,未更改则可以继续使用缓存,而不用向服务器发送数据请求,减轻服务器压力。

  • 缓存是优化系统性能的重要手段,HTTP 传输的每一个环节中都可以有缓存。
  • 服务器使用"Cache-Control"设置缓存策略,常用的是"max-age",表示资源的有效期。
  • 浏览器收到数据就会存入缓存,如果没过期就可以直接使用,过期就要去服务器验证是否仍然可用。
  • 验证资源是否失效需要使用"条件请求",常用的是"if-Modified-Since"和"If-None-Match",收到 304 就可以复用缓存里的资源。
  • 验证资源是否被修改的条件有两个:"Last-modified"和"ETag",需要服务器预先在响应报文里设置,搭配条件请求使用。
  • 浏览器也可以发送"Cache-Control"字段,使用"max-age=0"或"no_cache"刷新数据。

HTTP为了提高性能,在许多地方都用上了缓存。但是,只要是发送请求到服务器,就会存在损耗。那么,性能最高的HTTP请求,就是不用向服务器发送的请求。

相关推荐
Hellc0076 分钟前
MacOS升级ruby版本
前端·macos·ruby
前端西瓜哥15 分钟前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法
又写了一天BUG16 分钟前
npm install安装缓慢及npm更换源
前端·npm·node.js
cc蒲公英30 分钟前
Vue2+vue-office/excel 实现在线加载Excel文件预览
前端·vue.js·excel
Java开发追求者30 分钟前
在CSS中换行word-break: break-word和 word-break: break-all区别
前端·css·word
好名字082134 分钟前
monorepo基础搭建教程(从0到1 pnpm+monorepo+vue)
前端·javascript
pink大呲花42 分钟前
css鼠标常用样式
前端·css·计算机外设
Flying_Fish_roe42 分钟前
浏览器的内存回收机制&监控内存泄漏
java·前端·ecmascript·es6
小小竹子1 小时前
前端vue-实现富文本组件
前端·vue.js·富文本
小白小白从不日白1 小时前
react hooks--useReducer
前端·javascript·react.js