浏览器缓存机制有两种,一种为强缓存,一种为协商缓存。
对于强缓存,浏览器在第一次请求的时候,会直接下载资源,然后缓存在本地,第二次请求的时候,直接使用缓存。
对于协商缓存,第一次请求缓存且保存缓存标识与时间,重复请求向服务器发送缓存标识和最后缓存时间,服务端进行校验,如果失效则使用缓存。
强缓存方案
Exprires:服务端的响应头,第一次请求的时候,告诉客户端,该资源什么时候会过期。Exprires的缺陷是必须保证服务端时间和客户端时间严格同步。
Cache-control:max-age,表示该资源多少时间后过期,解决了客户端和服务端时间必须同步的问题,
协商缓存方案
If-None-Match/ETag:缓存标识,对比缓存时使用它来标识一个缓存,第一次请求的时候,服务端会返回该标识给客户端,客户端在第二次请求的时候会带上该标识与服务端进行对比并返回If-None-Match标识是否表示匹配。
Last-modified/If-Modified-Since:第一次请求的时候服务端返回Last-modified表明请求的资源上次的修改时间,第二次请求的时候客户端带上请求头If-Modified-Since,表示资源上次的修改时间,服务端拿到这两个字段进行对比。
面试官:嗯,那怎么使用强缓存?
我:浏览器在第一次请求资源的时候,服务端响应头里可以设置expires字段,该字段表示该资源的缓存过期时间,第二次请求的时候,如果时间还在该缓存时间之内,则会直接使用缓存,否则重新加载资源,这个expires字段有个缺陷,就是它必须服务端和客户端的时间严格同步才能生效,所以现在很多人不会使用改方案。另外一种方案是第一次请求资源的时候,服务端设置响应头cache-control: max-age,这样设置的意思是告诉浏览器,这个资源什么时候过期,等第二次请求资源的时候,判断是否超出了过期时间,如果没超出,直接使用缓存。
面试官:cache-control这个头是服务端设置的还是客户端设置的?
我:cache-control服务端设置的,cache-control的其他值,首先是public,客户端和服务端都可以缓存;然后是private,只能客户端缓存;no-store,不使用缓存;no-cache,使用协商缓存。
面试官:那你往下说,说一下协商缓存?
我:协商缓存有两种,一种是Last-Modified,就是第一次请求资源的时候,服务端会在响应头里面设置该字段,表示该资源的最后修改时间,浏览器第二次请求该资源的时候,会在请求头里面加上一个字段If-Modified-Since,值为第一次请求的时候服务端返回的Last-Modified的值,服务端会判断资源当时的最后更改时间与请求头里面的If-Modified-Since字段是否相同,如果相同,则告诉客户端使用缓存,否则重新下载资源。然后另外一种协商缓存时使用ETag,原理与Last-Modified类似,就是第一次请求的时候,服务端会根据资源的内容或者最后修改时间生成一个标识,然后在响应头里面设置为ETag返回给客户端,客户端第二次请求的时候会在请求头里面带上这个ETag,也就是在请求头里面加上If-None-Match字段,服务端接收到了ETag之后判断是否与原来第一次的标识相同,如果相同,则告诉客户端使用缓存。
前端缓存
前端缓存可分为两大类:http 缓存和浏览器缓存。我们今天重点讲的是 http 缓存,所以关于浏览器缓存大家自行去查阅。下面这张图是前端缓存的一个大致知识点:
image强缓存:浏览器直接从本地缓存中获取数据,不与服务器进行交互。
协商缓存:浏览器发送请求到服务器,服务器判定是否可使用本地缓存。
联系与区别:两种缓存方式最终使用的都是本地缓存;前者无需与服务器交互,后者需要。
HTTP缓存
首先是解决困扰人们的老大难问题:
一、什么是HTTP缓存 ?
http缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
常见的http缓存只能缓存get请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指GET请求。
http缓存都是从第二次请求开始的。第一次请求资源时,服务器返回资源,并在respone header头中回传资源的缓存参数;第二次请求时,浏览器判断这些请求参数,命中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否命中协商缓存,命中则返回304,否则服务器会返回新的资源。
1、http缓存的分类:
根据是否需要重新向服务器发起请求来分类,可分为(强制缓存,协商缓存) 根据是否可以被单个或者多个用户使用来分类,可分为(私有缓存,共享缓存) 强制缓存如果生效,不需要再和服务器发生交互,而协商缓存不管是否生效,都需要与服务端发生交互。下面是强制缓存和协商缓存的一些对比:
1.1、强制缓存
强制缓存在缓存数据未失效的情况下(即Cache-Control的max-age没有过期或者Expires的缓存时间没有过期),那么就会直接使用浏览器的缓存数据,不会再向服务器发送任何请求。强制缓存生效时,http状态码为200。这种方式页面的加载速度是最快的,性能也是很好的,但是在这期间,如果服务器端的资源修改了,页面上是拿不到的,因为它不会再向服务器发请求了。这种情况就是我们在开发种经常遇到的,比如你修改了页面上的某个样式,在页面上刷新了但没有生效,因为走的是强缓存,所以Ctrl + F5一顿操作之后就好了。 跟强制缓存相关的header头属性有(Pragma/Cache-Control/Expires)
这个Pragma和Cache-Control共存时的优先级问题还有点异议,我在不同的文章里发现:有的说Pragma的优先级更高,有的说Cache-Control高。为了搞清楚这个问题,我决定动手操作一波,首先我用nodejs搭建后台服务器,目的是设置缓存参数,具体代码如下:
image然后再浏览器上访问:http://localhost:8888
第一次访问时都是从后台返回的数据:
第二次访问时:
image image最终得出结论:
Pragma和Cache-control共存时,Pragma的优先级是比Cache-Control高的。
注意:
在chrome浏览器中返回的200状态会有两种情况:
1、from memory cache
(从内存中获取/一般缓存更新频率较高的js、图片、字体等资源)
2、from disk cache
(从磁盘中获取/一般缓存更新频率较低的js、css等资源)
这两种情况是chrome自身的一种缓存策略,这也是为什么chrome浏览器响应的快的原因。其他浏览返回的是已缓存状态,没有标识是从哪获取的缓存。
chrome浏览器:
imageFirefox浏览器:
image1.2、协商缓存
当第一次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期还或者它的属性设置为no-cache时(即不走强缓存),那么浏览器第二次请求时就会与服务器进行协商,与服务器端对比判断资源是否进行了修改更新。如果服务器端的资源没有修改,那么就会返回304状态码,告诉浏览器可以使用缓存中的数据,这样就减少了服务器的数据传输压力。如果数据有更新就会返回200状态码,服务器就会返回更新后的资源并且将缓存信息一起返回。跟协商缓存相关的header头属性有(ETag/If-Not-Match 、Last-Modified/If-Modified-Since)请求头和响应头需要成对出现
协商缓存的执行流程是这样的:当浏览器第一次向服务器发送请求时,会在响应头中返回协商缓存的头属性:ETag和Last-Modified,其中ETag返回的是一个hash值,Last-Modified返回的是GMT格式的最后修改时间。然后浏览器在第二次发送请求的时候,会在请求头中带上与ETag对应的If-Not-Match,其值就是响应头中返回的ETag的值,Last-Modified对应的If-Modified-Since。服务器在接收到这两个参数后会做比较,如果返回的是304状态码,则说明请求的资源没有修改,浏览器可以直接在缓存中取数据,否则,服务器会直接返回数据。
image image注意:
ETag/If-Not-Match是在HTTP/1.1出现的,主要是解决以下问题:
(1)、Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
(2)、如果某些文件被修改了,但是内容并没有任何变化,而Last-Modified却改变了,导致文件没法使用缓存
(3)、有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
1.3、私有缓存(浏览器级缓存)
私有缓存只能用于单独的用户:Cache-Control: Private
1.4、共享缓存(代理级缓存)
共享缓存可以被多个用户使用: Cache-Control: Public
二、为什么要使用HTTP缓存 ?
根据上面的学习可发现使用缓存的好处主要有以下几点:
1. 减少了冗余的数据传输,节省了网费。
2. 缓解了服务器的压力, 大大提高了网站的性能
3. 加快了客户端加载网页的速度
三、如何使用HTTP缓存 ?
一般需要缓存的资源有html页面和其他静态资源:
1、html页面缓存的设置主要是在<head>标签中嵌入<meta>标签,这种方式只对页面有效,对页面上的资源无效
1.1、html页面禁用缓存的设置如下:
<meta http-equiv="pragma" content="no-cache">
// 仅有IE浏览器才识别的标签,不一定会在请求字段加上Pragma,但的确会让当前页面每次都发新请求
<meta http-equiv="cache-control" content="no-cache">
// 其他主流浏览器识别的标签
<meta http-equiv="expires" content="0">
// 仅有IE浏览器才识别的标签,该方式仅仅作为知会IE缓存时间的标记,你并不能在请求或响应报文中找到Expires字段
1.2、html设置缓存如下:
<meta http-equiv="Cache-Control" content="max-age=7200" />
// 其他主流浏览器识别的标签
<meta http-equiv="Expires" content="Mon, 20 Aug 2018 23:00:00 GMT" />
// 仅有IE浏览器才识别的标签
2、静态资源的缓存一般是在web服务器上配置的,常用的web服务器有:nginx、apache。具体的配置这里不做详细介绍,大家自行查阅。
3、不想使用缓存的几种方式:
3.1、Ctrl + F5强制刷新,都会直接向服务器提取数据。
3.2、按F5刷新或浏览器的刷新按钮,默认加上Cache-Control:max-age=0,即会走协商缓存。
3.2、在IE浏览器下不想使用缓存的做法:打开IE,点击工具栏上的工具->Internet选项->常规->浏览历史记录 设置. 选择“从不”,然后保存。最后点击“删除”把Internet临时文件都删掉 (IE缓存的文件就是Internet临时文件)。
3.3、还有就是上面1、2中禁用缓存的做法
3.4、对于其他浏览器也都有清除缓存的办法
四、HTTP缓存的几个注意点
1、强缓存情况下,只要缓存还没过期,就会直接从缓存中取数据,就算服务器端有数据变化,也不会从服务器端获取了,这样就无法获取到修改后的数据。决解的办法有:在修改后的资源加上随机数,确保不会从缓存中取。
例如:
http://www.kimshare.club/kim/common.css?v=22324432
http://www.kimshare.club/kim/common.2312331.css
2、尽量减少304的请求,因为我们知道,协商缓存每次都会与后台服务器进行交互,所以性能上不是很好。从性能上来看尽量多使用强缓存。
3、在Firefox浏览器下,使用Cache-Control: no-cache 是不生效的,其识别的是no-store。这样能达到其他浏览器使用Cache-Control: no-cache的效果。所以为了兼容Firefox浏览器,经常会写成Cache-Control: no-cache,no-store。
4、与缓存相关的几个header属性有:Vary、Date/Age。
Vary:
vary本身是“变化”的意思,而在http报文中更趋于是“vary from”(与。。。不同)的含义,它表示服务端会以什么基准字段来区分、筛选缓存版本。
在服务端有着这么一个地址,如果是IE用户则返回针对IE开发的内容,否则返回另一个主流浏览器版本的内容。
格式:Vary: User-Agent
知会代理服务器需要以 User-Agent 这个请求首部字段来区别缓存版本,防止传递给客户端的缓存不正确。
Date/Age:
响应报文中的 Date 和 Age 字段:区分其收到的资源是否命中了代理服务器的缓存。
Date 理所当然是原服务器发送该资源响应报文的时间(GMT格式),如果你发现 Date 的时间与“当前时间”差别较大,或者连续F5刷新发现 Date 的值都没变化,则说明你当前请求是命中了代理服务器的缓存。
Age 也是响应报文中的首部字段,它表示该文件在代理服务器中存在的时间(秒),如文件被修改或替换,Age会重新由0开始累计。
浏览器缓存
下面说说最常用到的浏览器缓存有:cookie、sessionStorage、localStorage这三者的主要特征如下:
image课程总结
1、对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行协商缓存策略。
2、对于协商缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
下图是浏览器首次和再次发送http请求的执行流程图:
imageimage
作者:前端搬砖师Kim
链接:https://www.jianshu.com/p/227cee9c8d15
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论