前言:
web中有些场景下很多内容是不需要更改的,如果每次请求都向服务器请求那些一段时间内不会变动的内容数据,会造成不必要的带宽浪费。
有时候网络较差时,请求这些内容就需要花费很长时间来打开页面。
因此通过浏览器的缓存机制,协同服务器让浏览器缓存那些不需要频繁变动的资源就可以有效地降低流量消耗和响应时间。
http缓存指的是:当客户端向服务器请求资源时,会先查看浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取,而不需要从服务器中请求这个资源。
需要注意的是,常见的http缓存只能缓存GET请求的资源,所以下面说的请求缓存皆是指GET请求。
http缓存分类:根据是否需要向服务器发起请求把http缓存分为两个大类,强缓存和协商缓存。
http缓存都是从第二次请求开始的:
- 第一次请求资源时,客户端向服务器请求资源,服务器返回响应资源,并在response header中回传资源的缓存参数;
- 第二次请求资源时,浏览器判断这些请求参数,命中强缓存就返回200,使用磁盘缓存中的资源,不请求服务器,否则就把请求参数加到request header中传给服务器,看是否命中协商缓存,命中则返回304,使用缓存资源,若都没命中则服务器会返回新的资源。
强缓存是通过设置缓存的到期时间 expires
或者有效时间 max-age
,在有效时间内,缓存不会失效,浏览器直接从浏览器缓存中读取资源。当缓存数据库中没有所请求的资源,或所请求的资源已失效时,才会从服务端请求资源。
与强制缓存相关的请求响应头:
响应头,代表该资源的过期时间。但由于服务端时间和客户端时间可能有误差,这也将导致缓存命中可能有误差,另一方面,Expires是HTTP1.0的产物,故现在大多数使用Cache-Control替代。
请求/响应头,缓存控制字段,精确控制缓存策略。 Cache-Control有很多属性,不同的属性代表的意义也不同。
- private:客户端可以缓存
- public:客户端和代理服务器都可以缓存
- max-age=x:缓存内容将在x秒后失效
- no-cache:需要使用协商缓存来验证缓存数据
- no-store:所有内容都不会缓存
它的值有no-cache和no-store,表示意思同cacha-control,优先级高于cache-control和expires,即三者同时出现时,先看pragma -> cache-control -> expires。
协商缓存需要在服务器端对比资源是否修改,来判断是否可以使用缓存。若未改动,则返回304状态码,浏览器拿到此状态码就可以直接使用缓存数据了。否则服务器返回新资源。
由此可见,在协商缓存中,如何判断资源是否改动就尤为重要啦,现在主要有两种策略:Last-Modified 和 Etag
服务器在响应请求时,会告诉浏览器资源GMT格式的最后修改时间。
浏览器非第一次请求服务器时,请求头会包含 if-Modified-Since 字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有if-Modified-Since,则与被请求资源的最后修改时间进行对比,如果一致则返回304和响应报文头,此时浏览器只需要从缓存中获取资源即可。
- 如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK
- 如果没有被修改:那么只需传输响应header,服务器返回:304 Not Modified
Last-Modified 其实用起来不一定完全准确:
- Last-Modified标注的最后修改只能精确到秒级,如果某文件在1秒钟以内被修改多次的话,它将不能准确标注文件的修改时间
- 如果某些文件被修改了,但是内容并没有任何变化,而Last-Modified却改变了,导致文件没法使用缓存
- 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
因此,HTTP1.1推出了Etag,改进了这些问题。
服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识(生成规则由服务器决定)。
非第一次请求服务器时,浏览器的请求报文头部(Request Headers)会包含 If-None-Match 字段,后面的值为在缓存中获取的标识。服务器接收到此报文后就用 If-None-Match 的值与被请求资源的唯一标识进行对比。
- 不同,则说明资源被改动,则返回状态码200,服务器返回新资源。
- 相同,说明资源未修改,则返回状态码304,浏览器直接从缓存中获取数据资源。
不过etag也存在缺点,就是每次生成表示字符串会增加服务器的开销。所以要如何使用last-modified和etag还需要根据具体需求进行权衡。
好了,理论部分说完了,下面我们自己动手尝试一下
我用nodejs开了一个服务器,用来获取get请求的响应,并在其中设置http缓存的相关属性,网页部分就是一个简单的加载图片,初学nodejs写接口,没有很完善,只是为了弄明白缓存怎么用,仅供参考。
nodejs中get请求部分的代码:
app.get("/api/getPic", (req, res) => {
res.setHeader("Cache-Control", "public,max-age=120"); //max-age设置的2分钟
let date = new Date(Date.now() + 5000).toUTCString(); //Expires过期时间设置了5分钟后
res.setHeader("Expires", date);
let data = JSON.stringify({
msg: "请求成功",
result: [
{
url:
"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=350525183,1430160676&fm=11&gp=0.jpg"
}
]
});
res.send(data);
});
发送一次请求,可以看到强缓存的配置已经设置好了(默认带了协商缓存的etag,但是我也不知道为什么。。。。。。):
之后我立即刷新页面,此时变成酱紫了,status Code是200,后边显示from disk cache,此时的图片资源即从磁盘缓存中获取,不请求服务器:
两分钟后,再次刷新页面,此时status code显示304,即请求的服务器,请求的资源没有被更改,命中的协商缓存:
由于默认带了 Etag协商缓存,所以协商缓存部分就没有手写,感兴趣的小伙伴可自行编写设置 Last-Modified 和 Etag 属性值。
网络请求的size会出现三种情况
- from memory cache (内存缓存)
- from disk cache (磁盘缓存)
- 资源数值大小
- 显示数值的状态码为200, 直接从服务器下载最新资源
- from memory cache 不请求网络资源,资源在内存当中,一般js脚本,字体,图片会存放在内存当中
- from disk cache 不请求网络资源,在磁盘当中,一般非脚本会存在内存当中,如css
我们将访问和刷新分为以下三种情况: