浏览器缓存
浏览器缓存
注:MS总被问的问题,也算是高频问题,即使不问,在问你浏览器加载URL的时候可以在讲完CDN、DNS后把这个加上,然后就是域名解析得到IP地址之后,浏览器与服务器进行连接,中间涉及tcp/ip的连接与断开,可以谈连接三次握手、断开四次挥手,如果是https协议的话,可以说下多的是什么东西等等,浏览器请求资源可以把浏览器缓存的理解加上,浏览器的进程和线程是怎么工作的,之后也就是浏览器对请求回来的资源进行解析,其中htmlPaser、cssPaser对文件的解析然后合并成renderTree,然后绘制到页面上的过程,这里还可以加上重排重绘的理解(什么情况会造成重排重绘,怎样避免重排重绘等),这样回答浏览器中输入URL后都做了什么基本上回答的点都有了。(其实我能想到回答的点也不全)如果还了解什么是后端的分布式等知识,把这些都加上估计会更加分反正我是不怎么懂。
当浏览器请求一个网站的时候,会加载各种各样的资源,比如:HTML文档、图片、CSS和JS等文件。对于一些不经常变的内容,浏览器会将它们保存在本地的文件中,下次访问相同的网站的时候,直接加载这些资源,加速访问。这些被浏览器保存的文件就被称为缓存(不是指cookie或者localstorage)。第一次打开网站的后,如果再次刷新页面。会发现浏览器加载的众多资源中,有一部分size有具体数据值,然而还有一部分请求并没有显示文件的大小(比如JS文件、CSS文件等),而是显示from disk cache 或者 from memory cache 字样。这就说明了,该资源直接从本地硬盘或者浏览器内存中读取,并没有请求到服务器。
浏览器缓存机制可分为强缓存和协商缓存
- 浏览器在加载资源时,先根据这个资源的一些HTTP Header判断它是否命中强缓存,强缓存如果命中浏览器直接从自己的缓存中读取资源,不会发请求到服务器。
- 当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一个HTTP Header验证这个资源是否命中协商缓存,如果协商缓存命中服务器会将这个请求返回,但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源
- 强缓存和协商缓存的共同点是:如果命中都是从客户端缓存中加载资源而不是从服务器端加载资源;区别:强缓存不发送请求到服务器,协商缓存会发送请求到服务器。
- 当协商缓存也没有命中的时候,浏览器直接从服务器端加载资源数据(网络请求)。
强缓存(强制缓存)
Expires是服务器端响应请求时用来规定资源的失效时间,Expires是HTTP1.0的产物。
缺点:到期时间由服务端生成,但是客户端时间可能跟服务端时间有误差,这就会导致缓存命中的误差。所以HTTP1.1的版本中,使用Cache-Control替代。在当下的前端实践中我们继续使用expires的唯一目的就是向下兼容。
Cache-Control常见的取值有private、public、no-cache、max-age、no-store,默认为private。
private:内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可以缓存)
public:所有内容都将被缓存(客户端和代理服务器端都可缓存)
max-age=xxx:缓存的内容将在xxx秒后失效
s-maxage:用于表示cache服务器上缓存的有效时间,并只对public缓存有效。s-maxage仅在代理服务器中生效,客户端我们只考虑max-age
no-cache:每一次发起请求都不会再去询问浏览器的缓存情况,而是直接向服务端去确认该资源是否过期(即协商缓存)
no-store:所有的内容都不会被缓存,强缓存(强制缓存)、协商缓存(对比缓存)都不会被触发,所有的内容都不会被缓存或Internet临时文件中
must-revalidation/proxy-recalidation:如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证。
Pragma
Pragma:no-cache,跟Cache-Control: no-cache相同。
Pragma: no-cache兼容http 1.0 ,Cache-Control: no-cache是http 1.1提供的。
因此,Pragma: no-cache可以应用到http 1.0 和http 1.1,而Cache-Control: no-cache只能应用于http 1.1。
Pragma是旧产物,已经逐步抛弃,有些网站为了向下兼容还保留了这两个字段。
优先级从高到低是 Pragma -> Cache-Control -> Expires
Expires、Cache-Control、Pragma为响应头字段
协商缓存(对比缓存)
Last-Modified / If-Modified-Since
Last-Modified:服务器在响应请求时,告诉浏览器资源的最后修改时间。
eg:Last-Modified: Fri, 27 Oct 2017 06:35:57 GMT
If-Modified-Since:再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。服务器收到请求后发现有请求头 If-Modified-Since则与被请求资源的最后修改时间进行比对。若资源的最后修改时间大于 If-Modified-Since,说明资源又被改动过,则响应资源内容,返回状态码200;若资源的最后修改时间小于或等于If-Modified-Since,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache。
eg:If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT
Etag / If-None-Match(优先级高于 Last-Modified / If-Modified-Since)
Etag:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定),这个标识字符串是基于文件内容编码的,只要文件内容不同,它们对应的 Etag就是不同的,反之亦然。因此 Etag 能够精准地感知文件的变化。
eg:ETag: W/"2a3b-1602480f459"
If-None-Match:再次请求服务器时,通过此字段通知服务器客户段缓存数据的唯一标识。服务器收到请求后发现有请求头If-None-Match 则与被请求资源的唯一标识进行比对,不同,说明资源又被改动过,则响应整片资源内容,返回状态码200;相同,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache。
eg:If-None-Match: W/"2a3b-1602480f459"
HTTP 1.1 中 Etag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题:
- Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内被修改多次的话,它将不能准确标注文件的修改时间;
- 如果某些文件会被定期生成,但有时内容并没有任何变化,但 Last-Modified 却改变了,导致文件没法使用缓存;
- 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形;
Etag也有弊端:
Etag 的生成过程需要服务器额外付出开销,会影响服务端的性能,这是它的弊端。因此启用 Etag 需要我们审时度势。正如我们刚刚所提到的——Etag 并不能替代Last-Modified,它只能作为 Last-Modified 的补充和强化存在。
Etag为响应头字段、If-None-match为请求头字段;
Last-Modified为响应头字段、If-Modified-Since为请求头字段;
补充:
我们总是说Etag优先级高于Last-Modified ,但是Etag 其实只是 Last-Modified 的补充和强化。这句话应该是这样理解的:首先,决定用哪个字段是服务器自己的事情,和强制缓存不太一样,所以并没有谁优先级高于谁的说法。但是,作为服务器来说,我直接判断Etag肯定是更快更高效的方法。这也是为什么说它优先级高的原因。那是不是有了Etag就可以不用 Last-Modified了呢,显然是不可以的。毕竟 Etag会消耗服务器性能。所以,针对文件变动,用Last-Modified不能帮我们判断文件是否有效的,我们就用Etag来补充和强化,而针对文件变化比较大,Last-Modified就能很显著的表现的,我们可以不用Etag。
如果什么缓存策略都没设置,那么浏览器会怎么处理?
对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified值的 10% 作为缓存时间。
用户操作 | Expires/Cache-Control | Last-Modified/Etag |
地址栏回车 | 有效 | 有效 |
页面链接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效 | 有效 |
ctrl + F5刷新 | 无效(重置Cache-Control=no-cache) | 无效(请求头丢弃该选项) |
以自己现在的努力程度,还没有资格和别人拼天赋