
本文共 5850 字,大约阅读时间需要 19 分钟。
前言
最近在准备面试,发现浏览器的缓存机制是一个常考的点,也是作为前端开发工程师必须要掌握的,这篇文章结合几篇参考资料特意整理出来,方便记忆。以下是两篇博文。建议看第一篇,第二篇有点问题
《精简的缓存机制》
强制缓存(200)和协商缓存(304)的区别
1. 什么是缓存?
浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存,以便下一次访问时重复使用,节省带宽,提高访问速度,降低服务器压力
http缓存机制主要在http响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、If-Modified-Since、Etag。
HTTP 1.0协议中的。简而言之,就是告诉浏览器在约定的这个时间前,可以直接从缓存中获取资源(representations),而无需跑到服务器去获取。
另:Expires因为是对时间设定的,且时间是Greenwich Mean Time (GMT),而不是本地时间,所以对时间要求较高。
2. 浏览器是如何判断是否使用缓存的
第一次请求:
第二次请求相同网页:
3. 缓存的类别
浏览器缓存分为强缓存和协商缓存
强缓存:浏览器不会像服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
200 form memory cache :
不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。
200 from disk cache:
不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。
优先访问memory cache,其次是disk cache,最后是请求网络资源
协商缓存: 向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
4. 强缓存和协商缓存的header参数
强缓存:
Expires:过期时间,如果设置了时间,则浏览器会在设置的时间内直接读取缓存,不再请求
Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
cache-control:除了该字段外,还有下面几个比较常用的设置值:
-
(1) max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;
-
(2) s-maxage:和max-age是一样的,不过它只针对代理服务器缓存而言;
-
(3)public:指示响应可被任何缓存区缓存;
-
(4)private:只能针对个人用户,而不能被代理服务器缓存;
-
(5)no-cache:强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。服务器接收到 请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。这个很容易让人产生误解,使人误 以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。
-
(6)no-store:禁止一切缓存(这个才是响应不被缓存的意思)。
协商缓存:
Last-Modifued/If-Modified-Since和Etag/If-None-Match是分别成对出现的,呈一一对应关系
Etag/If-None-Match:
Etag:
Etag是属于HTTP 1.1属性,它是由服务器(Apache或者其他工具)生成返回给前端,用来帮助服务器控制Web端的缓存验证。通过发送的
etag
的值和服务端重新生成的etag
的值进行比对,如果一致代表资源没有改变,服务端返回正文为空的响应,告诉浏览器从缓存中读取资源。
Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
If-None-Match:
当资源过期时,浏览器发现响应头里有Etag,则再次像服务器请求时带上请求头if-none-match(值是Etag的值)。服务器收到请求进行比对,决定返回200或304
Last-Modifued/If-Modified-Since:
Last-Modified:
浏览器向服务器发送资源最后的修改时间
If-Modified-Since:
当资源过期时(浏览器判断Cache-Control标识的max-age过期),发现响应头具有Last-Modified声明,则再次像服务器请求时带上头if-modified-since,表示请求时间。服务器收到请求后发现有if-modified-since则与被请求资源的最后修改时间进行对比(Last-Modified),若最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;若最后修改时间较旧(小),说明资源无新修改,响应HTTP 304 走缓存。
Last-Modifued/If-Modified-Since的时间精度是秒,而Etag可以更精确。
Etag优先级是高于Last-Modifued的,所以服务器会优先验证Etag
《缓存机制》
1、缓存机制
首先我们来总体感知一下它的匹配流程,如下:
-
浏览器发送请求前,根据请求头的expires和cache-control判断是否命中(包括是否过期)强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。
-
没有命中强缓存规则,浏览器会发送请求,根据请求头的last-modified和etag判断是否命中协商缓存,如果命中,直接从缓存获取资源。如果没有命中,则进入下一步。
-
如果前两步都没有命中,则直接从服务端获取资源。
2、强缓存
强缓存:不会向服务器发送请求,直接从缓存中读取资源。
2.1 强缓存原理
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的情况主要有三种(暂不分析协商缓存过程),如下:
- 第一次请求,不存在缓存结果和缓存标识,直接向服务器发送请求
- 再次请求,存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
那么强制缓存的缓存规则是什么?
当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires
和Cache-Control
,其中Cache-Control
优先级比Expires
高。 2.1.1、 Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间
,需要和Last-modified
结合使用。Expires
是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
2.1.2、 Cache-Control
在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:
-
public:所有内容都将被缓存(客户端和代理服务器都可缓存)
-
private:所有内容只有客户端可以缓存,
Cache-Control
的默认取值 -
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
-
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
-
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效
需要注意的是,
no-cache
这个名字有一点误导。设置了no-cache
之后,并不是说浏览器就不再缓存数据,只是浏览器在使用缓存数据时,需要先确认一下数据是否还跟服务器保持一致,也就是协商缓存。而no-store
才表示不会被缓存,即不使用强制缓存,也不使用协商缓存
2.1.3、设置
强缓存需要服务端设置expires
和cache-control
。
nginx
代码参考,设置了一年的缓存时间: 浏览器的缓存存放在哪里,如何在浏览器中判断强制缓存是否生效?这就是下面我们要讲到的from disk cache
和from memory cache
。
2.2、from disk cache和from memory cache
细心地同学在开发的时候应该注意到了Chrome的网络请求的Size会出现三种情况from disk cache(磁盘缓存)
、from memory cache(内存缓存)
、以及资源大小数值。
状态 | 类型 | 说明 |
---|---|---|
200 | form memory cache | 不请求网络资源,资源在内存当中,一般脚本、字体、图片会存在内存当中 |
200 | form disk ceche | 不请求网络资源,在磁盘当中,一般非脚本会存在内存当中,如css等 |
200 | 资源大小数值 | 从服务器下载最新资源 |
304 | 报文大小 | 请求服务端发现资源没有更新,使用本地资源 |
浏览器读取缓存的顺序为memory –> disk。
以访问https://github.com/xiangxingchen/blog
为例 我们第一次访问时 关闭标签页,再此打开
https://github.com/xiangxingchen/blog
时
F5刷新时
简单的对比一下
3、协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
协商缓存生效,返回304和Not Modified
协商缓存失效,返回200和请求结果
注意:当第一次对服务器发起HTTP请求的时候,服务器就一起返回了Expires、Cache-Control、Last-Modifued,Etag这些字段
3.1、Last-Modified和If-Modified-Since
-
浏览器首先发送一个请求,让服务端在
response header
中返回请求的资源上次更新时间,就是last-modified
,浏览器会缓存下这个时间。 -
然后浏览器再下次请求中,
request header
中带上if-modified-since
:[保存的last-modified的值]
。根据浏览器发送的修改时间和服务端的修改时间进行比对,一致的话代表资源没有改变,服务端返回正文为空的响应,让浏览器中缓存中读取资源,这就大大减小了请求的消耗。
由于last-modified依赖的是保存的绝对时间,还是会出现误差的情况:
-
保存的时间是以秒为单位的,1秒内多次修改是无法捕捉到的;
-
各机器读取到的时间不一致,就有出现误差的可能性。为了改善这个问题,提出了使用etag。
3.2、ETag和If-None-Match
etag
是http
协议提供的若干机制中的一种Web
缓存验证机制,并且允许客户端进行缓存协商。生成etag常用的方法包括对资源内容使用抗碰撞散列函数,使用最近修改的时间戳的哈希值,甚至只是一个版本号。 和last-modified
一样.
-
浏览器会先发送一个请求得到
etag
的值,然后再下一次请求在request header
中带上if-none-match
:[保存的etag的值]
。 -
通过发送的
etag
的值和服务端重新生成的etag
的值进行比对,如果一致代表资源没有改变,服务端返回正文为空的响应,告诉浏览器从缓存中读取资源。
etag能够解决last-modified的一些缺点,但是etag每次服务端生成都需要进行读写操作,而last-modified只需要读取操作,从这方面来看,etag的消耗是更大的。
注意,还有一个412是这样子的
etag
单纯的以修改时间来判断还是有缺陷,比如文件的最后修改时间变了,但内容没变。对于这样的情况,我们可以使用etag来处理。
etag的方式是这样:服务器通过某个算法对资源进行计算,取得一串值(类似于文件的md5值),之后将该值通过etag返回给客户端,客户端下次请求时通过If-None-Match或If-Match带上该值,服务器对该值进行对比校验:如果一致则不要返回资源。
If-None-Match和If-Match的区别是:If-None-Match:告诉服务器如果一致,返回状态码304,不一致则返回资源
If-Match:告诉服务器如果不一致,返回状态码412
If-Match: ETag-value
告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段。
If-Match 的一个应用场景是,客户端走PUT方法向服务端请求上传/更替资源,这时候可以通过 If-Match 传递资源的ETag。
二者对比
-
精确度上:
Etag
要优于Last-Modified
。 -
优先级上:服务器校验优先考虑
Etag
。 -
性能上:
Etag
要逊于Last-Modified
4、用户行为对浏览器缓存的影响
-
打开网页,地址栏输入地址: 查找
disk cache
中是否有匹配。如有则使用;如没有则发送网络请求。 -
普通刷新 (F5):因为 TAB 并没有关闭,因此
memory cache
是可用的,会被优先使用(如果匹配的话)。其次才是disk cache
。 -
强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有
Cache-control:no-cache
(为了兼容,还带了Pragma:no-cache
),服务器直接返回 200 和最新内容。
5、总结
发表评论
最新留言
关于作者
