阅读 6880

面试官问我:说说你对浏览器缓存的理解

缓存类型

强缓存

定义:

当发起HTTP请求时,不会向服务器进行请求,只要当前时间在缓存有效期内,则直接从客户端缓存中获得,当缓存过期之后,才会真正想服务器发起请求重新获得资源。

特征

打开开发者工具,从Network中可查看到,HTTP请求返回的状态码为200并且后面跟着from memory cache 或 from disk cache。

如何设置为强缓存:

由于HTTP版本的升级,在HTTP1之前,指定资源到期的时间是xpires,注意:到期时间是指在响应请求返回的服务器的时间,当客户端再次发起请求时,会拿本地时间与缓存过期时间比较,如果在有效期内,则直接从客户端本地读取缓存数据,这也就意味着:你可以通过更改本地系统时间,强制控制缓存失效。

在请求头或响应头中设置Cache-Control,Cache-Control的作用是指定请求或响应的缓存机制,它具有单向性,也就是说,如果你在请求中头设置了该属性,它却不一定会在响应头中存在。它的设置方式有两种,一种是通过客户端设置,一种是在服务端接收到请求后在服务端设置,两种设置方法的可选值不同,根据 MDN 解释,具体情况如下:

可选值中含义具体可参考MDN https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control

强缓存有什么作用:

1)对于页面上耗费带宽且不常更改的资源,一旦下载,可直接将其缓存在浏览器,用户再次使用,可大大降低加载等待时间,提升用户体验;

2)针对弱网或网络不稳定的用户,强缓存可预留出足够的带宽,让给主资源请求,防止因主资源请求超时导致页面无法正常显示。

弊端

强缓存的优点就是,只要客户端存在有效期内的资源,就不再从服务端获取资源,而他的优点也正是他的弊端,如果服务器资源更新了,或者修正了一个bug,而强制缓存时间太长,就会造成客户端信息更新落后的问题,怎么办呢?能不能有一个这样的解决方法:每次请求资源,先问问服务器:我需要重新拿资源吗?服务器通过标识对比,缓存数据没有更改,缓存时间也没过期,然后告诉客户端,继续用存储的数据吧,我这没啥变更。这样既解决了缓存问题,也解决了更新问题。虽然依然是每次都发送请求,请求数量没减少,但是却可以降低数据量传输。有这样的方案吗?有。叫什么?协商缓存。

小结

强缓存下,无论是从客户端获取的资源还是从服务器端获取的更新资源,在获取成功时,返回的状态码都是200,唯一能区分的,是跟在状态码后面有没有from memory cache 或 from disk cache.在有效期内,只有第一次会真正向服务器发送请求获取数据

协商缓存

如何设置为协商缓存:

response header里面的设置

etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT
复制代码

etag:每个文件有唯一一个,资源变更则etag变更。是一个文件hash,加上的目的就是为了对比客户端和服务端是否一致,判断是有更新,不一致则认为是更新了,一致则认为未更改。 last-modified:文件的修改时间,精确到秒。

也就是说,每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,这时候对客户端来说,每次请求都是要进行协商缓存了,即:

发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->没过期-->返回304状态码-->客户端用缓存的老资源。

这就是一条完整的协商缓存的过程。

当服务端发现资源真的过期的时候,会走如下流程:

发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->过期-->返回200状态码-->客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。

所以协商缓存步骤总结:

请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。 如果资源没更改,返回304,浏览器读取本地缓存。 如果资源有更改,返回200,返回最新的资源。

为什么要有etag?

你可能会觉得使用last-modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要etag呢?HTTP1.1中etag的出现(也就是说,etag是新增的,为了解决之前只有If-Modified的缺点)主要是为了解决几个last-modified比较难解决的问题:

1)一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新get;

2)某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),if-modified-since能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);

3)某些服务器不能精确的得到文件的最后修改时间。

小结

协商缓存首次请求成功会返回状态码200,在接下来,如果文件未更改,则会返回状态码为304,因此可以通过判断状态码值来确定资源是重新更新的还是来自缓存。另外,由于标识资源是否有效的last-modify和etag都是由服务端确定,因此,协商缓存不会受制于本地系统时间的更改。

习题:

看完上文,可以思考以下问题:

强缓存和协商缓存的设置属性不同,是不是意味着可以同时设置两种两种缓存方式?

如果可以同时设置强缓存和协商缓存,每一种缓存的有效和失效两种状态,那么,在缓存类型和有效状态组成的4种组合中,客户端是从哪获取数据呢?

欢迎在评论区留下你的答案

点赞+关注,让自己跑的更快一点。

参考文章

www.jianshu.com/p/9c95db596…

juejin.im/post/684490…