前言

之前无论是在网上看一些帖子,或是在实际工作中一定都会接触到浏览器缓存这个东西,尤其在开发过程中总是会不知不觉就设置缓存,我的做法也很简单粗暴,直接command + shift + r(强制刷新),我管这个叫大刷新,这样页面上的东西就保证了都是从服务器端拿到的最新数据。那么这篇文章我们就结合代码好好看一下这个浏览器缓存是什么东西。

缓存的好处当然毋庸置疑,在你再次访问某个页面的时候,显然很多资源是不需要重新加载的,因为他们根本就没变过,与其浏览器去服务器重新请求并下载资源,当然不如直接就用上一次没变过的资源啦。

浏览器的缓存策略

1.协商缓存
2.强制缓存
3.启发式缓存

搭建一个hello world级别的express服务器

服务器端代码:
Screen-Shot-2020-07-12-at-10.40.16-AM

页面html代码:html中只引入一个js文件,其中在页面上显示时间戳,其中的import.js文件只log出一个"我被引入了"
Screen-Shot-2020-07-12-at-10.41.48-AM
代码很简单。

协商缓存

Literally speacking(从字面意义上来说),协商协商,肯定需要商量着来,就是服务器和浏览器之间商量个条件出来,各自遵守这个条件来进行缓存,来决定是否需要缓存。

Last-Modified与If-Modified-Since

话不多说,我们直接把上一节的服务器跑起来,localhost8080打开,熟悉的节奏,devtools打开,network panel打开,第一次请求如下:
Screen-Shot-2020-07-12-at-11.01.15-AM
上图看到请求状态都为200。
然后刷行一下(不要强刷)
截图如下:
Screen-Shot-2020-07-12-at-11.05.58-AM

此时我们能够看到:
第一次的request中的If-Modified-Since = 第二次的request中的If-Modified-Since = 第二次请求response的last-Modified字段。

其背后的原理是
1.在第一次请求文件的时候浏览器会将该文件返回的last-modified记录下来,作为第二次请求的if-modified-since字段。
2.而在服务器端,每次修改文件的时候,都会记录下来并作为response的last-Modified字段,跟随文件一同发给浏览器。
3. 浏览器第二次请求将上次收到的last-modified作为此次request的if-modified-since字段发给服务器,服务器收到后比较该字段与文件的last-modified字段是否匹配,如果匹配则返回304,告诉浏览器你可以从cache中取出继续用,如果不匹配的话将新文件发过去并返回200

如果此时我们将文件进行修改之后进行第三次请求的话,会发现:第三次请求的If-Modified-Since还是之前的值,但是response的last-Modified字段进行了变化,且返回码成为200,证明服务器端发来了新的文件

ETag与If-None-Match

正常来说如果有Last-Modified与If-Modified-Since两个字段已经可以实现协商缓存了,但是还有一种情况,因为有可能你的文件的变更频率太快,快到小于1s了,那么Last-Modified与If-Modified-Since的时间粒度比较就不适合了,需要更精确的比较了。

现在服务器文件中只需要加入一行代码:

    app.enable('etag')

重启服务器之后,发第一次请求,然后再刷新一次。此时发现请求和回复的ETag与If-None-Match值是一样的。我们在html文件中随便加些东西修改文件之后,再再请求一次,比较ETag与If-None-Match发现第三次请求的If-None-Match和第二次ETag和第二次的If-None-Match是相同的,但是第三次回复的ETag和他们都不同了,所以返回码此时也变成了200了,意思是需要返回新的文件啦。

优先级是ETag与If-None-Match > Last-Modified与If-Modified-Since, 同时存在时, 前者覆盖后者.

强制缓存

强制缓存字面上理解就是强迫你去缓存,不像协商缓存有的商量,强制缓存会设置一个时间,在这个时间范围之内,你去用缓存,超过了这个时间的话,你就得去重新下载文件啦。
强制缓存主要是靠Cache-Control和Expires这两个字段

Expires字段

这个字段是设置一个绝对时间,浏览器请求的时候将客户端的时间发给服务器,服务器将发过来的时间和本地时间进行

    res.setHeader("Expires", new Date(Date.now() + 30000).toUTCString());

设置header其他的参数都是利用上面的代码实现,本文不再赘述。
但是有一个问题就是用户可以通过设置本地时间导致判断出现问题所以引出下一个字段

cache-control: max-age

max-age设置的是相对缓存时间开始之后的时间范围,因此不再受日期不准确情况的影响。
此外max-age字段是no-store的时候,强制缓存和协商缓存都不会被触发。

优先级:
在优先级上:max-age>Expires。当两者同时出现在响应头时,Expires将被max-age覆盖。

总结

在总体的优先级上:强缓存 > 协商缓存
最后来一张图,转自掘金
Screen-Shot-2020-07-12-at-3.38.35-PM

reference: https://juejin.im/post/5c6c9c99f265da2d896326ae