浏览器的缓存机制是一个让人又爱又恨的东西。它可以极大的提升用户体验;但是也因为从缓存里读取数据展示的却不是最新的,造成错误数据,因此在开发的过程中要将缓存处理一番。
什么是缓存:
把一个已经请求过得web资源【html页面、图片、js、数据】拷贝一份放在浏览器中。等下一次请求的时候,如果是相同的URL,浏览器会根据缓存机制决定是直接从缓存中读取数据还是向服务器发送数据请求。
页面想要被浏览器缓存下来的条件:
- 含有完整的过期时间控制头信息【http协议报头】;
- 浏览器已经使用过这个缓存的内容,并且在某一次的请求中检查过服务器上的资源是否发生改变。
使用缓存有哪些好处:
- 减少网络宽带消耗【当浏览器从缓存中读取数据的时候,只需要一点点的网络流量就可以了,从而降低运营成本】;
- 降低服务器压力【当浏览器使用缓存记录不从服务器哪记录的时候,相对的减少了服务器的被请求次数。】;
- 减少网络延迟【缓存的时候极大的提升了页面的打开速度,有了更好的用户体验】。
浏览器的缓存机制是什么:
对于浏览器的缓存来讲,主要的规则是在http协议头部和html页面的meta标签中定义的。他们分别从浏览器验证方面和服务器验证方面来判断页面是否需要从服务器获取新的数据。
浏览器验证:主要是判断缓存的过期机制。
过期机制:是指缓存内容的在一段时间内是有效的,过了这个时间就不会被直接应用而是去请求新的数据。
服务器验证:当服务器返回资源的时候控制头信息可以带上这个资源的实体标签,Last-modified/Etag(Entity Tag),它可以用来作为浏览器再次请求过程中的校验标识,如果前后两次的标识不匹配,就说明缓存内容已经不是最新的,浏览器需要重新获取相应资源数据。
浏览器缓存机制的控制
目前想要控制浏览器的缓存机制有两种办法
- 使用meta标签;
- 使用缓存有关的http报头【也就是设置http的header部分】。
Meta标签控制是指web开发人员在html页面加入meta标签,代码如下:
上述代码的作用是告诉浏览器当前页面不需要被缓存,每次加载的时候都直接从服务器获取数据。但是这种禁用缓存的形式很有限主要表现于:
1、仅IE浏览器才能识别这段meta标签代码,其他的大多数主流浏览器只识别”cache-control”;
2、在IE浏览器中识别到该meta标签,但是不一定会在请求字段中加上pragma,但的确会让当前页面每次都发起新的请求,但是只是当前页面的内容,页面内引入的资源不会被重新请求。
使用缓存有关的http头部信息是指在http请求时在它的协议头部设置不同的参数。
我们看下面的图片来了解一下常见的和缓存有关的配置项:
上面的图片看完后,简单的分一下类,再来进行比较相对更容易理解一些。
cache-control/expires属于浏览器校验部分;
Last-modified/Etag属于服务器校验部分。
cache-control和expires
他们的作用是一样的,都是表示当前缓存资源的有效期,通过判断是否在有限期来控制浏览器是直接从缓存中读取数据还是重新发送请求到服务器读取数据。只不过cache-control的选择更多一些,更加细致,Expires存在的问题是:该过期的时间是相对于服务器设定的,如果客户端时间和服务器端不一致【比如用户重置了系统的时间】就会导致缓存跟期待的效果出现偏差。所以expires在http1.1更新之后就基本被弃用了。
Last-modified和Etag
是浏览器访问同样url的时候,浏览器依然会向服务器发出请求,询问所请求的页面数据是否修改了,如果没有修改,服务器直接返回304,浏览器从缓存中读取数据;如果修改了,服务器会把新的页面数据发送给浏览器。
Last-modified和Etag的区别在于:前者校验的是页面数据最后修改的时间,以秒为单位,并且如果服务器校验后发现last-modified是最新的时间,则不会重新返回新的last-modified;后者校验的是页面数据生成的唯一标识符,如果页面数据内容有改变,那么Etag也会跟着改变,如果页面没有改变,服务器依然会把新生成的Etag返回给浏览器。两者相比后者更加精准,因为会存在以下几种情况是last-modified不能应付的:
- 一些文件也许会周期性的更改,但是内容并不改变,改变的只是时间。这种时候我们是不希望浏览器重新从服务获取数据的;
- 某些文件被频繁改变,如果在1s内更改多次的话,那么last-modified是无法完全监测的;
- 一些服务器不一定能精确的得到文件最后修改的时间。
如果last-modified和Etag一起使用的话,服务器会先验证last-modifie,一致的情况下才会继续比对Etag,如果两个都一致则会向浏览器发送304,只要其中有一项不一致就会向浏览器返回新的页面数据以及最新的last-modified值和Etag值。
Last-modified/Etag与cache-control/expires的区别:
Last-modified/Etag会向服务器发出请求,来判断是否从缓存中读取页面数据;cache-control/expires则不会,如果检测到缓存内容还在有效期内,则浏览器直接从缓存中读取,不会经过服务器。
当两者一起使用的时候,cache-control/expires的优先级会高于Last-modified/Etag,也就是说当浏览器发现缓存数据还在有效期时,是不会向服务器发出校验请求的。
说到这有两个关于缓存的小概念:
强缓存:用户发送的请求,直接从浏览器缓存中获取,不发送请求到服务器,不与服务器发生交互行为。【cache-control/expires】
协商缓存:用户发送请求,请求被发送到服务器之后,由服务器判定是否从缓存中获取资源。【Last-modified/Etag】
两者共同点:浏览器获取的数据最后都是从缓存中取得。
两者区别:从名字就可以看出,强缓存不与服务器发生交互,而协商缓存则需要与服务器发生交互。
以上整体对比完了,下面说一下他们在实际中的应用:
expires
由于在http1.1的时候基本就弃用了,所以这里就不说了。
cache-control
Http1.1中的标准,可以看作为Expires的补充,使用的是相对时间的概念。
简单介绍一下Cache-Control的属性设置:
- max-age:设置缓存的最大有效时间,单位为秒(s),max-age会覆盖Expires;
- s-maxage:只用于公用缓存,比如CDN缓存(s->share)。与max-age的区别是:max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage会覆盖max-age和Expires;
- public:响应会被缓存,并且在用户间共享。默认是public;
- private:响应只作为私有的缓存,不能在用户间共享。如果需要HTTP认证,响应会自动设置为private。
- no-cache:指定不缓存响应,表明资源不进行缓存。但是设置了no-cache之后不代表浏览器不缓存,而是在缓存前要向服务器确认资源是否被更改。
- no-store:绝对禁止缓存
- must-revalidate:如果页面过期,则去服务器进行获取。
如果上面的内容不太好记的话,我从另外一位大神那里copy了一张图,流程非常清晰可以帮助加深理解:
Last-modified
Last-modified是服务器发送给浏览器时的字段名称;当浏览器使用Last-modified验证,向服务器发送的时候用的是另外一个名字:if-modified-since。
Etag
Etag也是服务器发送给浏览器时的字段名称;当浏览器使用Last-modified验证,向服务器发送的时候用的是另外一个名字:if-none-match。
下面简单说一下实际从缓存中获取资源的大概过程:
- 浏览器判定是否有缓存:
“客户端缓存”就是指用户设备中的本地资源。不同浏览器缓存文件的地址不同。浏览器判定是否有缓存的方式是通过浏览器读取本地缓存的地方看是否有对应的响应。(即查看文件是否存在) - 看缓存是否过期:
客户端保留了一个response header
Date字段表明此时缓存服务器的时间。
判断缓存是否过期的步骤如下:
1)查看是否有cache-control的max-age/s-maxage,如果有,则用服务器时间date值+max-age/s-maxage的秒数计算出新的过期时间,将当前时间与过期时间进行比较,判断是否过期
2)查看是否有cache-control的max-age/s-maxage,如果没有,则用expires作为过期时间比较 - 进行服务器校验,判断是返回304还是返回新的页面数据:
既Etag校验和last-modified校验,如果两个全部一致的话返回304;否则的返回新的数据和Etag值及last-modified值方便下次校验。
以上纯属个人理解,如果有问题或者哪里不对的话,还请留言批注,我会积极修改和处理。