是什么
浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)储存在本地(内存或者硬盘)。当下一次请求要发出的时候,如果是相同的URL,浏览器会根据缓存机制决定是直接使用先前存储的资源,还是向源服务器再次发送请求。
缓存机制(流程图)
(盗的糊图)
简单理解
初次接触这个流程图的时候确实觉得有点唬人。
其实解决两个疑问就好了:
首先是本地缓存阶段,如何判断资源是否过期? 其次是协商阶段,如何判断本地资源是否和服务器的资源是否一样?
问题1:怎么判断是否过期失效呢?
简单来说,就像食品有效期一样,判断依据就是:食品生产日期+保质期 是否大于 现在的时间
。
而换成浏览器缓存,判断依据就是:缓存更新周期 + 缓存最后修改时间 是否大于 现在的时间
。
在缓存字段中,相关字段有:Expires,Cache-control中的max-age,LastModify,Etag,他们的作用关系图如下:
Expires
Expires是http/1.0的东西,作用是web服务器的响应字段,描述的是缓存的过期时间,缺点是返回的到期时间是服务器的到期时间,如果浏览器跟服务器的时区跨度很大,就会造成时差。所以在http/1.1开始,使用Cache-control的max-age代替
注意:Cache-control是http/1.1为了弥补Expires而加入的,当两者都存在时,Cache-control优先级更高。
Cache-control:
更新周期max-age
Cache-control选项
可缓存性:
- public: 表明响应可以被任何对象(包括请求的客户端,代理服务器)缓存
- private:只有用户自己的浏览器可以进行缓存,公共服务器不可以
- no-cache(默认选项):强制浏览器在使用cache拷贝之前先提交一个http请求到服务器进行确认(可以用mate标签里面的content属性)
- only-if-cache:表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否更新的拷贝
到期设置:
- max-age=60:设置缓存存储的最大周期,超过这个时间(last-)被视为过期,单位为秒。
其他设置:
- no-store:不进行cache。
最后修改时间
这里有max-age属性,我们可以知道缓存的最大周期,那我怎么知道过没过期呢?总该有个开始时间我才能算出来吧?
有的,有两种,一种是Last-Modified,一种是Etag。
Last-modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间
Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器来定),默认有对文件的索引节(INode),大小(Size),和最后修改时间(MTime)进行hash后得到的
我们可以通过这两个标识的最后修改时间再加上max-age来得知缓存是否过期。
为什么有两种呢?Last-modified已经足够浏览器知道本地的缓存副本是否够新,为什么还需要Etag呢?
- 因为Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
- 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存,而Etag会根据资源计算的哈希值来判断资源有没有变化。
- 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形。
所以Last-modified和Etag一起使用时,会先验证Etag。
小结:在这一步中,如果缓存没有过期,则直接使用本地缓存。
问题2:如何判断本地资源是否和服务器资源一样
判断本地资源是否和服务器资源一样的方法就是:问服务器。
这里有两种不同的情况
- 当使用expires字段的时候,请求时再带上expires字段就好了
- 当使用的是last-modified字段的时候,请求时带上If-Modified-Since,表示请求时间
- 当使用的是Etag字段的时候,请求时带上If-None-Match
Last-modified/If-Modified-Since
Last-modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间
If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上If-Modified-Since,表示请求时间,web服务器收到请求后发现有头If-Modified-Since,则与被请求资源的最后修改时间比较。若最后修改时间较新,说明资源已经有变动,则响应资源,HTTP200;如果最后修改时间较久,那么资源没更新,使用缓存,返回HTTP304.
Etag/If-None-Match
Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器来定),默认有对文件的索引节(INode),大小(Size),和最后修改时间(MTime)进行hash后得到的
If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
浏览器缓存相关知识点
1.from disk cache 和 from memory cache
从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。
- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
这里详细可以参考:
2.用户行为对浏览器缓存的影响
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
- 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control:no-cache(为了兼容,还带了 Pragma:no-cache),服务器直接返回 200 和最新内容。
3.实践
为 HTML 指定 Cache-Control 或 Expires
为 HTML 内容设置 Cache-Control 或 Expires 可以将 HTML 内容缓存起来,避免频繁向服务器端发送请求。前面讲到,在页面 Cache-Control 或 Expires 头部有效时,浏览器将直接从缓存中读取内容,不向服务器端发送请求。
1.<meta http-equiv="Cache-Control" content="max-age=7200">
2.<meta http-equiv="Expires" content="Mon,20Jul201623:00:00GMT">复制代码
合理设置Etag和Last-Modified
合理设置 Etag 和 Last-Modified 使用浏览器缓存,对于未修改的文件,静态资源服务器会向浏览器端返回304,让浏览器从缓存中读取文件,减少 Web 资源下载的带宽消耗并降低服务器负载。
<meta http-equiv="last-modified" content="Sun,05 Nov 2017 13:45:57 GMT">
1.经常变化的资源,比如模板文件html,这类文件的请求URL通常不会变化,但是内容会经常变化,因为每次项目更新,插入到html文件中的js或者css可能有更新。
Cache-Control: no-cache
协商缓存响应头设置成no-cache,配合协商缓存一起使用,浏览器每次都会去服务器核对资源有没有更新,如果资源没有更新,那么会返回一个304的状态码,不会返回真实的响应体,这种形式相比于命中强缓存,虽然无法节省那次网络请求,但是如果命中了协商缓存,会节省返回的响应体体积,也算是性能优化的一种。
2.不经常变化的资源,比如打包出来的第三方库JS,给这类资源设置一个较长的缓存周期,比如一年
Cache-Control: max-age=31536000
设置了这个字段后,就命中了强缓存策略,那么如果在一年内我们修改了这个文件,如何让用户端能够强制请求最新资源呢?答案就是哈希,在webpack打包时给这类资源加上contentHash,一旦资源内容有更新,打包出来的资源的哈希值也会更新,用户端去加载资源时,去缓存中找不到符合这个哈希值版本的资源文件,自然会去服务端请求最新的资源。