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

缓存类型

强缓存

定义:

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

特征

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

如何设置为强缓存:

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

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

可选值中含义具体可参考 MDNhttps://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… https://juejin.cn/post/6854573208444600333