浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)。可是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,Web开发者能够在HTML页面的<head>节点中加入<meta>标签,代码以下:css
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
上述代码的做用是告诉浏览器当前页面不被缓存,每次访问都须要去服务器拉取。使用上很简单,但只有部分浏览器能够支持,并且全部缓存代理服务器都不支持,由于代理不解析HTML内容自己。下面主要介绍HTTP协议定义的缓存机制。html
浏览器缓存一直是一个让人又爱又恨的存在,一方面极大地提高了用户体验,而另外一方面有时会由于读取了缓存而展现了“错误”的东西,而在开发过程当中想方设法地想把缓存禁掉。若是没据说过浏览器缓存或者不知道浏览器缓存的用处,能够先浏览一下这篇文章->Web缓存的做用与类型 。jquery
那么浏览器缓存机制究竟是如何工做的呢?核心就是把缓存的内容保存在了本地,而不用每次都向服务端发送相同的请求,设想下每次都打开相同的页面,而在第一次打开的同时,将下载的js、css、图片等“保存”在了本地,而以后的请求每次都在本地读取,效率是否是高了不少?真正的浏览器工做的时候并非将完整的内容保存在本地,各类浏览器都有不一样的方式,譬如firefox是一种相似innodb的方式存储的key value 的模式,在地址栏中输入 about:cache 能够看见缓存的文件,chrome会把缓存的文件保存在一个叫User Data的文件夹下。可是若是每次都读取缓存也会存在必定的问题,若是服务端的文件更新了呢?这时服务端就会和客户端约定一个有效期,譬如说服务端告诉客户端1天内我服务端的文件不会更新,你就放心地读取缓存吧,因而在这一天里每次遇到相同的请求客户端都开心地能够读取缓存里的文件。可是若是一天过去了,客户端又要读取该文件了,发现和服务端约定的有效期过了,因而就会向服务端发送请求,试图下载一个新的文件,可是颇有可能服务端的文件其实并无更新,其实仍是能够读取缓存的。这时该怎么判断服务端的文件有没有更新呢?有两种方式,第一种在上一次服务端告诉客户端约定的有效期的同时,告诉客户端该文件最后修改的时间,当再次试图从服务端下载该文件的时候,check下该文件有没有更新(对比最后修改时间),若是没有,则读取缓存;第二种方式是在上一次服务端告诉客户端约定有效期的同时,同时告诉客户端该文件的版本号,当服务端文件更新的时候,改变版本号,再次发送请求的时候check一下版本号是否一致就好了,如一致,则可直接读取缓存。web
而事实上真正的浏览器缓存机制大抵也是如此,接下来就能够分别对号入座了。chrome
须要注意的是,浏览器会在第一次请求完服务器后获得响应,咱们能够在服务器中设置这些响应,从而达到在之后的请求中尽可能减小甚至不从服务器获取资源的目的。浏览器是依靠请求和响应中的的头信息来控制缓存的。浏览器
Expires和Cache-Control就是服务端用来约定和客户端的有效时间的。缓存
好比如上一个响应头,Expires规定了缓存失效时间(Date为当前时间),而Cache-Control的max-age规定了缓存有效时间(2552s),理论上这两个值计算出的有效时间应该是相同的(上图好像不一致)。Expires是HTTP1.0的东西,而Cache-Control是HTTP1.1的,规定若是max-age和Expires同时存在,前者优先级高于后者。Cache-Control的参数能够设置不少值,譬如(参考浏览器缓存机制):安全
2016-07-21 补充:服务器
no-cache 和 no-storepost
no-cache表示必须先与服务器确认返回的响应是否被更改,而后才能使用该响应来知足后续对同一个网址的请求。所以,若是存在合适的验证令牌 (ETag),no-cache 会发起往返通讯来验证缓存的响应,若是资源未被更改,能够避免下载。
相比之下,no-store更加简单,直接禁止浏览器和全部中继缓存存储返回的任何版本的响应 - 例如:一个包含我的隐私数据或银行数据的响应。每次用户请求该资源时,都会向服务器发送一个请求,每次都会下载完整的响应。
public和 private
若是响应被标记为public,即便有关联的 HTTP 认证,甚至响应状态码没法正常缓存,响应也能够被缓存。大多数状况下,public不是必须的,由于明确的缓存信息(例如max-age)已表示 响应能够被缓存。
相比之下,浏览器能够缓存private响应,可是一般只为单个用户缓存,所以,不容许任何中继缓存对其进行缓存 - 例如,用户浏览器能够缓存包含用户私人信息的 HTML 网页,可是 CDN 不能缓存。
max-age
该指令指定从当前请求开始,容许获取的响应被重用的最长时间(单位为秒) - 例如:max-age=60表示响应能够再缓存和重用 60 秒。
而Last-Modified/If-Modified-Since就是上面说的当有效期事后,check服务端文件是否更新的第一种方式,要配合Cache-Control使用。好比第一次访问个人主页simplify the life,会请求一个jquery文件,响应头返回以下信息:
而后我在主页按下ctrl+r刷新,由于ctrl+r会默认跳过max-age和Expires的检验直接去向服务器发送请求(下文再探讨各类刷新后如何读取缓存),咱们看看请求截图:
请求头中包含了If-Modified-Since项,而它的值和上次请求响应头中的Last-Modified一致,咱们发现这个日期是在遥远的2013年,也就是说这个jquery文件自从2013年的那个日期后就没有再被修改过了。将If-Modified-Since的日期和服务端该文件的最后修改日期对比,若是相同,则响应HTTP304,从缓存读数据;若是不相同文件更新了,HTTP200,返回数据,同时经过响应头更新last-Modified的值(以备下次对比)。
而ETag/If-None-Match则是上文大话中说的第二种check服务端文件是否更新的方式,也要配合Cache-Control使用。实际上ETag并非文件的版本号,而是一串能够表明该文件惟一的字符串(Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后获得的。),当客户端发现和服务器约定的直接读取缓存的时间过了,就在请求中发送If-None-Match选项,值即为上次请求后响应头的ETag值,该值在服务端和服务端表明该文件惟一的字符串对比(若是服务端该文件改变了,该值就会变),若是相同,则相应HTTP304,客户端直接读取缓存,若是不相同,HTTP200,下载正确的数据,更新ETag值。
看如上截图,与服务器约定的直接读取本地缓存的时间过了,就会向服务器发送新的请求,请求头中带If-None-Match项,该字符串值会在服务端进行匹配,很显然,并无什么变化(看响应头的ETag值),因而响应HTTP304,直接读取缓存。或许你会发送该请求也有If-Modified-Since项,若是二者同时存在,If-None-Match优先,忽略If-Modified-Since。或许你会问为何它优先?二者功能类似甚至相同,为何要同时存在?HTTP1.1中ETag的出现主要是为了解决几个Last-Modified比较难解决的问题:
固然并非全部请求都能被缓存。
没法被浏览器缓存的请求:
浏览器缓存过程还和用户行为有关,譬如上面提到的,打开个人主页simplify the life,有个jquery的请求,若是直接在地址栏按回车,响应HTTP200(from cache),由于有效期还没过直接读取的缓存;若是ctrl+r进行刷新,则会相应HTTP304(Not Modified),虽然仍是读取的本地缓存,可是多了一次服务端的请求;而若是是ctrl+shift+r强刷,则会直接从服务器下载新的文件,响应HTTP200。
经过上表咱们能够看到,当用户在按F5进行刷新的时候,会忽略Expires/Cache-Control的设置,会再次发送请求去服务器请求,而Last-Modified/Etag仍是有效的,服务器会根据状况判断返回304仍是200;而当用户使用Ctrl+F5进行强制刷新的时候,只是全部的缓存机制都将失效,从新从服务器拉去资源。
更多能够参考浏览器缓存机制
盗图浏览器缓存机制,两张图很清晰