现如今,作为一个前端工程师,无论你去哪家公司面试都会被问到 cache controll
, 304
等相关的面试题,今天小编就带着你一起来系统梳理下浏览器缓存里的那些事。废话不多说,先给大家上图:
相信大家看到这张图,有种豁然开朗的感觉。既然我们要梳理浏览器读取缓存的机制,首要的是,我们得先搞清楚浏览器是怎么读取缓存的:
If-Modified-Since
和 If-None-Match
字段,让服务器判断是否应该读缓存文件,如果服务器判断文件没有变化,就会返回所谓的 304
,并且没有响应实体。这时候浏览器就会读取缓存文件。接下来我们就一一梳理从页面加载,到获取最终资源的各类情况:
Ctrl + F5
(win)|| cmd + shift + R
(mac) vs F5
(win)|| cmd + R
(mac) || 新开窗口F5
触发的 HTTP 请求的请求头中通常包含If-Modified-Since
或 If-None-Match
字段,或者两者兼有,此时如果服务器判断文件有没有发生变化,就会返回 304
响应, 也就没有跳过缓存;
Ctrl + F5
触发的 HTTP 请求的请求头中通常包含Pragma: no-cache
或 Cache-Control: no-cache
字段,或者两者兼有,服务器看到 no-cache
这样的值就会把最新的文件响应过去.也就跳过了缓存.
那么问题就来了,服务器又是从何得知文件有没有变化呢?服务器的判断依据是啥?okay,我们再次回到第一张图中看一眼。没错,映入我们眼帘的就是它们 Etag
和 Last-Modified
(最新修改时间)。
在此,我们先需要了解两个概念:
Etag: 实体标签(EntityTag)是唯一标识了一个组件的一个特定版本的字符串,是 web服务器
用来确认组件缓存组件有效性的一种机制,通常使用组件的某些属性来构造它。用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配
Last-Modified: 原始服务器(部署我们代码的服务器)通过 Last-Modified
响应头来返回组件的最新修改时间。
当我们在地址栏回车、浏览器前进后退、页面链接跳转中进行期中任何一个操作时,浏览器都会去确认是否有缓存,在缓存没有过去的情况下(也就是 Cache-Control
的 maxAge
和 Expires
没有失效的情况下)会直接浏览器缓存,也就是所谓的无请求读取缓存。接下来就是重点了,擦亮你的金丝边眼镜看好了: 当没有缓存或者缓存过期的情况下, 浏览器发送请求头中包含 If-Modified-Since:服务器资源上次更新时间
和 If-None-Match:etag
字段的HTTP请求。来个例子,让大家看的更直白些;
eg:没有缓存的请求
// request
GET /i/yahoo/gif HTTP 1.1
Host: us.yimg.com
// response
HTTP 1.1 200 OK
Last-Modified:Tue,12 Dec 200603:03:59 GMT
ETag:”10c24bc-4ab-457elc1f“
在此请求相同组件
// request
HTTP 1.1
Host: us.yimg.com
If-Modified-Since:Tue,12 Dec 200603:03:59 GMT
If-None-Match:”10c24bc-4ab-457elc1f“
// response
HTTP 1.1 304 Not Midified
那么这时候,又有人要问了,既然能拿到服务器资源最后一次的更新时间,为什么还要引入 Etag
,只要新的需求发布了,我能确保能拿到最新的资源不就行了么!why Etag?
Etag
主要是为了解决 Last-Modified
无法解决的一些问题:
- 一些文件页也许会周期性更改,但是他的内容并不变化(只是修改时间会变),这个时候我们就不希望客户端认为这个文件被修改了,二重新拉去最新的资源。
If-Modified-Since
能检查的粒度是妙级的,这种修改无法判断,这种修改通过Last-Modified
是不会变化的。(或者说UNIX记录MTIME只能精确到秒)- 某些服务器不能精确地得到文件的最后修改时间
Etag
存在的潜在问题
上边定义也提到了, Etag
使用组件的某些属性来构造它,但是有些属性对于特定的部署了网站的服务器来说是唯一的。那么当使用集群服务器的时候,浏览器从一台服务器获取了原始组件,之后又向另外一台服务器发起条件GET
请求,Etag
就会出现不匹配的情况.
解决 Etag
潜在问题的最佳事件方案
- 如果只使用
Last-Modified
不会有问题的情况下,可以考虑移除。- 确定使用
Etag
的话,在配置的时候,移除能够影响到组件集群服务器验证的属性,例如只包含组件大小和时间戳。
浏览器可以在内存、硬盘中开辟一个空间用以保存请求资源的副本。我们经常在 Dev tool
里面看到 Memory Cache
(内存缓存) 和 Disk Cache
(硬盘缓存),值得就是缓存的位置。
请求一个资源时,会按照 Server Worker -> Memory Cache -> Disk Cache -> Push Cache -> Push Cache
依次查找缓存,如果命中则使用缓存,否则发起网络请求。
对于缓存的使用,很多人真是的爱恨交加。开发的时候,总是希望立即生效? 上线时又希望文件尽可能的被缓存,来提高性能。特别是对缓存机制还不清楚的情况下,更是痛不欲生。现在我们已经有所了解了,那就应该趁热打铁,打造一个更好的缓存策略是首当其冲的紧要事情了。
到此,HTTP缓存那些事就先介绍到这里了,以上是个人实际开发中遇到的问题,进行的一系列梳理,希望有助于大家,不对的地方还望大神多多指正。