HTTP 缓存机制作为 Web 应用性能优化的重要手段,对于从事 Web 开发的同学们来说,应该是知识体系的基础环节,也是想要成为前端架构的必备技能。
缓存的作用
我们为什么使用缓存,是因为缓存可以给我们的 Web 项目带来以下好处,以提高性能和用户体验。
加快了浏览器加载网页的速度;
减少了冗余的数据传输,节省网络流量和带宽;
减少服务器的负担,大大提高了网站的性能。
由于从本地缓存读取静态资源,加快浏览器的网页加载速度是一定的,也确实的减少了数据传输,就提高网站性能来说,可能一两个用户的访问对于减小服务器的负担没有明显效果,但如果这个网站在高并发的情况下,使用缓存对于减小服务器压力和整个网站的性能都会发生质的变化。
缓存规则简介
为了方便理解,我们认为浏览器存在一个缓存数据库,用于存储缓存信息(实际上静态资源是被缓存到了内存和磁盘中),在浏览器第一次请求数据时,此时缓存数据库没有对应的缓存数据,则需要请求服务器,服务器会将缓存规则和数据返回,浏览器将缓存规则和数据存储进缓存数据库。
当浏览器地址栏输入地址后请求的 index.html 是不会被缓存的,但 index.html 内部请求的其他资源会遵循缓存策略,HTTP 缓存有多种规则,根据是否需要向服务器发送请求主要分为两大类,强制缓存和协商缓存。
强制缓存
1、强制缓存流程
强制缓存是第一次访问服务器获取数据后,在有效时间内不会再请求服务器,而是直接使用缓存数据,强制缓存的流程如下。
2、强制缓存判断到期时间
那么如何判断缓存是否到期呢?其实还是根据第一次访问时服务器的响应头来实现的,在 HTTP 1.0 版本和 HTTP 1.1 版本有所不同。
在 HTTP 1.0 版本,服务器使用的响应头字段为 Expires,值为未来的绝对时间(时间戳),浏览器请求时的当前时间超过了 Expires 设置的时间,代表缓存失效,需要再次向服务器发送请求,否则都会直接从缓存数据库中获取数据。
在 HTTP 1.1 版本,服务器使用的响应头字段为 Cache-Control,有多个值,意义各不相同。
private:客户端可以缓存;
public:客户端和代理服务器都可以缓存(对于前端而言,可以认为与 private 效果相同);
max-age=xxx:缓存的内容将在 xxx 秒后过期(相对时间,秒为单位);
no-cache:需要使用协商缓存(后面介绍)来验证数据是否过期;
no-store:所有内容都不会缓存,强制缓存和协商缓存都不会触发。
Cache-Control 的值中最常用的为 max-age=xxx,缓存本身就是为了数据传输的优化和性能而存在的,所以 no-store 几乎不会使用。
注意:在 HTTP 1.0 版本中,Expires 字段的绝对时间是从服务器获取的,由于请求需要时间,所以浏览器的请求时间与服务器接收到请求所获取的时间是存在误差的,这也导致了缓存命中的误差,在 HTTP 1.1 版本中,因为 Cache-Control 的值 max-age=xxx 中的 xxx 是以秒为单位的相对时间,所以在浏览器接收到资源后开始倒计时,规避了 HTTP 1.0 中缓存命中存在误差的缺点,为了兼容低版本 HTTP 协议,正常开发中两种响应头会同时使用,HTTP 1.1 版本的实现优先级高于 HTTP 1.0。
3、通过 Network 查看强制缓存
我们通过 Chrome 浏览器的开发者工具,打开 NetWork 查看强制缓存的相关信息。
上面是百度网站 Logo 图片的响应,我们可以清楚的看到,其中兼容了 HTTP 1.0 和 HTTP 1.1 版本,并使用强制缓存存储了 10 年。
下面看一看通过缓存取出的数据在 Network 中与其他资源的区别。
其实缓存的储存是内存和磁盘两个位置,由当前浏览器本身的策略决定,比较随机,从内存的缓存中取出的数据会显示 (from memory cache),从磁盘的缓存中取出的数据会显示 (from disk cache)。
4、NodeJS 服务器实现强制缓存