前言
没有缓存的浏览器是无源之水、无根之木,需要通过不等次数的网络来回获取资源,而网络情况是不确定的,所以网络请求一直被我们视为洪水猛兽。
所以不管是为了加快用户访问速度、改善用户体验,还是节约流量,存储之前获取的资源并留待以后重复利用势在必行。
HTTP缓存主要是根据HTTP协议的约定,主要还是服务端通过HTTP头部字段等进行控制,客户端接收到相关字段并进行相应缓存存储以及命中逻辑。
所以到底有哪些重要字段呢?
header
1. Cache-Control
- max-age
设置缓存最大有效期,单位为s,定义的是时间长短,需要配合时间字段使用,如Date。在max-age设定的有效期内,浏览器发出的请求会直接命中HTTP缓存,不会进入网络堆栈。 - s-maxage
用法同max-age,不过该字段只用于共享缓存(比如CDN缓存),当处理代理缓存时,如果存在s-maxage,那么其会覆盖掉max-age和expires。同理如果在s-maxage有效期内,即使CDN资源发生了改变,浏览器请求也不会进入网络堆栈。 - no-cache
不缓存响应,表示必须先和服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续请求命中缓存。场景一般是资源变化比较迅速,所以强制启用ETag进行资源验证,如果ETag等验证通过,则可以返回304 - no-store
绝对禁止浏览器以及中间缓存存储任何版本的响应,场景一般是个人隐私数据或者安全性比较高的银行数据等,强制要求每次请求资源必须进行用户验证和网络来回。 - public
相对于private是默认值,即使存在HTTP用户认证等限制,缓存也会被响应,并且允许多用户共享,比如CDN。 - private
只作为私有的缓存,不能再用户间共享,不允许任何中间缓存对其进行缓存,比如CDN。如果要求HTTP认证,响应会自动设置为private。
2. Expires
缓存过期时间,是具体的时间点,即绝对时间,数值上等于max-age + new Date().getTime() 。其优先级相对Cache-Control略低,主要用作兼容旧浏览器,具体作用与Cache-Control的max-age类似。
3. Last-Modified
语义为资源最后修改时间,经常配合Cache-Control使用,是检查资源更新的重要字段。当浏览器请求资源并且缓存已经不再有效期内或者no-cache,那么我们需要进行网络来回确认资源是否修改,此时浏览器根据Last-Modified会生成if-Modify-Since,服务端接收比对后如果没有修改则返回304,直接使用缓存,如果修改了,则请求服务器最新资源。
4. ETag
服务端根据一定规则生成的hash字符串、UUID或类似物,用来进行资源状态唯一标识,其作用过程如下:
其实作用与Last-Modified有重叠,我们只需比对修改时间是否一致即可,为何还要比对ETag字符串呢?
因为Last-Modified存在如下问题:
- 某些服务器不能精确得到资源的最后修改时间
- 如果资源修改非常平凡,在秒级别之下的时间内进行修改,而Last-Modified只能精确到秒
- 当资源最后时间修改过了,但内容没变时,ETag不会变化,可以有效区分这种情况
大概就讲这四个关键的header,如果想了解其他的header,可以看我另一篇博客HTTP协议知多少
总结
所以综上所述,HTTP缓存的流程为:
在制定缓存策略时,您需要牢记下面这些技巧和方法:(来自谷歌开发者《HTTP缓存》)
- 使用一致的网址:如果您在不同的网址上提供相同的内容,将会多次获取和存储这些内容。提示:请注意,网址区分大小写。
- 确保服务器提供验证令牌 (ETag):有了验证令牌,当服务器上的资源未发生变化时,就不需要传送相同的字节。
- 确定中间缓存可以缓存哪些资源:对所有用户的响应完全相同的资源非常适合由 CDN 以及其他中间缓存进行缓存。
- 为每个资源确定最佳缓存周期:不同的资源可能有不同的更新要求。为每个资源审核并确定合适的 max-age。
- 确定最适合您的网站的缓存层次结构:您可以通过为 HTML 文档组合使用包含内容指纹的资源网址和短时间或 no-cache 周期,来控制客户端获取更新的速度。
- 最大限度减少搅动:某些资源的更新比其他资源频繁。如果资源的特定部分(例如 JavaScript 函数或 CSS 样式集)会经常更新,可以考虑将其代码作为单独的文件提供。这样一来,每次获取更新时,其余内容(例如变化不是很频繁的内容库代码)可以从缓存获取,从而最大限度减少下载的内容大小。