在面试或工作中,前端缓存可能是一个常见的问题。当网页加载速度变慢时,我们通常会考虑设置缓存或使用 CDN 来优化性能。但是,尽管这是一个常见的解决方案,你可能仍然没有深入了解缓存技术。因此,在本文中,我们将深入探讨前端缓存。
首先,我们需要了解前端缓存的分类。下面是一个图示:
强缓存
在这之前,我们先来看看第一次打开网站,在无缓存的情况下,从服务器获取资源。
上图是在没有缓存的情况下加载,可以从大小和时间列看到从服务器拉取的资源大小和时间。再来看看第一次之后有缓存的情况下加载。
可以看到,加载的速度明细变快了,加载时间缩短了50%左右(受资源大小、网络情况影响可能不同),同时从Memory Cache缓存获取资源大小和时间都变成了0,Disk Cache获取会有一定的解析时间。当然,Memory Cache和Disk Cache都是强缓存的标识。
可以来看下强缓存的报文,重点看看Expires和Cache-Control。
Expires响应头包含日期/时间,即在此时候之后,响应过期。时间是一个GMT格式的时间绝对值。Expires是http 1.0的规范,如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age" 指令,那么Expires头会被忽略。由于Expires缓存时间还会根据系统时间去判断,所以修改系统时间可以使缓存失效。
Cache-Control是http 1.1新增的字段,通常用 "max-age" 来设置缓存,设置的时间是一个相对值,单位 "秒",标识当前响应头Date之后的多少秒内有效。除了 "max-age",Cache-Control 还有其他值,多个值可以用逗号分隔。
-
public:表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容。(例如:1.该响应没有"max-age"指令或Expires消息头;2.该响应对应的请求方法是POST。) -
private:表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容,比如:对应用户的本地浏览器。 -
no-cache:该值会命令浏览器在每次使用 URL 的缓存版本前都必须与服务器重新验证。 -
no-store:缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存。 -
s-maxage:覆盖 "max-age" 或者Expires头,但是仅适用于共享缓存 (比如各个代理),私有缓存会忽略它。 -
no-transform:不得对资源进行转换或转变。Content-Encoding、Content-Range、Content-Type等 HTTP 头不能由代理修改。例如,非透明代理或者如Google's Light Mode可能对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。"no-transform" 指令不允许这样做。
协商缓存
与强缓存不同的是,协商缓存需要请求到服务器,服务器判断当前缓存资源是否有更新,若有效,返回状态码304,使用缓存资源。
协商缓存通常是在强缓存失效的情况下发起,协商缓存的标识有etag和last-modified,etag优先级高于last-modified,两者同时存在时只有etag生效。
当再次请求资源的时候,请求头会带上两个参数if-modified-since和if-none-match,分别对应last-modified和etag。首先会进行判断if-none-match对应的etag是否存在,如果不存在,会根据if-modified-since的时间与服务器资源最后修改时间是否一致,从而判断是否走协商缓存。