前言
对每个前端开发者来说都避不开前端缓存,那么前端缓存都有哪些,又该如何去设置呢?
前端缓存只要分为 HTTP 缓存和浏览器缓存,下面我们分别来介绍一下
HTTP 缓存
HTTP 缓存又分一下两种:
- 强缓存
- 协商缓存
两者的主要区别是使用本地缓存的时候,是否需要向服务器验证本地缓存是否依旧有效。顾名思义,协商缓存,就是需要和服务器进行协商,最终确定是否使用本地缓存。
强缓存
当浏览器向服务器发起请求时,服务器会将缓存规则放入 HTTP 响应报文的 HTTP 头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是 Expires 和 Cache-Control,其中 Cache-Control 优先级比 Expires 高。
Expires
Expires 是 HTTP/1.0 控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于 Expires 的值时,直接使用缓存结果。
到了 HTTP/1.1,Expire 已经被 Cache-Control 替代,原因在于 Expires 控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义。
Cache-Control
符合缓存策略时,服务器不会发送新的资源,但不是说客户端和服务器就没有会话了,客户端还是会发请求到服务器的。
Cache-Control 除了在响应中使用,在请求中也可以使用。我们用开发者工具来模拟下请求时带上 Cache-Control:勾选 Disable cache,刷新页面,可以看到 Request Headers 中有个字段 Cache-Control: no-cache。
在 HTTP/1.1 中,Cache-Control 是最重要的规则,主要用于控制网页缓存,主要取值为:
- public:所有内容都将被缓存(客户端和代理服务器都可缓存)
public
出现再响应首部, 则即使他有关联的 HTTP 验证, 甚至响应状态代码代码通常无法缓存, 也可以缓存响应。大多数情况下,public
不是必须的, 因为明确的缓存信息 (例如max-age
) 已表示响应是可以缓存 - private:所有内容只有客户端可以缓存,Cache-Control 的默认取值
相比之下, 浏览器可以缓存
private
响应。不过这些响应通常只为单个用户缓存, 因此不允许任何中间缓存对其进行缓存, 例如, 用户的浏览器可以缓存包含用户私人信息的 HTML 网页, 但 CDN 不能缓存 - no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
no-cache
表示必须先与服务器确认返回的响应是否发生了变化, 然后才能使用响应来满足后续对同意网址的请求。因此如果存在合适的验证令牌 (ETag
),no-cache
会发起往返通信来验证缓存的响应, 但如果资源未发生变化, 则可避免下载 - no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
no-store
表示直接禁止浏览器以及所有中间缓存存储任何版本的返回响应, 例如, 包含个人隐私数据或银行业务数据的响应。每次用户请求该资产时, 都会向服务器发送请求, 并下载完整的响应 - max-age=xxx (xxx is numeric):缓存内容将在 xxx 秒后失效
max-age
值定义了文档的最大使用期——从第一次生成文档到文档不再新鲜, 无法使用为止, 最大的合法生存时间(以秒为单位)
协商缓存
仅仅是以缓存过期了并不意味着他和原始服务器目前处于活跃状态的文档有实际的区别, 这只是意味着到了要进行核对的时间了, 这种情况被称为 协商缓存, 说明缓存需要询问原始服务器是否发生变化
- 如果再验证显示内容发生了变化, 缓存会获取一份新的文档副本, 并将其存储在旧文档的位置上, 然后将文档发送给客户端。
- 如果再验证内容没有发生变化, 缓存只需要获取新的首部, 包括一个新的过期日期, 并对缓存中的首部进行更新, 并对缓存中的首部进行更新就行了
用条件方法进行再验
HTTP 的条件方法可以高效的实现再验证。HTTP 允许缓存向原始服务器发送一个条件 GET, 请求服务器只有在文档与缓存中现有的副本不同时, 才回送对象主体, 对于缓存在验证来说最有用的 2 个首部时
-
If-Modified-Since: <date>
:
如果从指定日期之后, 文档被修改了, 就执行请求的方法。可以与 Last-Modfied
服务器响应首部配合使用, 只有在内容修改后与已缓存版本有所不同的时候才去获取内容
-
If-None-Match:<tags>
:
服务器可以为文档提供特殊的标签 (ETag
), 而不是将其与最近修改日期向匹配, 这些标签就像序列号一样。如果已缓存标签与服务器文档中的标签有所不同,If-None-Match
首部就会执行所请求的方法
If-Modified-Since: / Last-Modified
具体流程如下:
- 客户端第一次向服务器发起请求, 服务器将最后的修改日期 (
Last-Modified
) 附加到所提供的文档上去 - 当再一次请求资源时间, 如果没有命中强缓存, 在执行在验证时, 会包含一个
If-Modifed-Since
首部, 其中携带有最后修改已缓存副本的日期:If-Modified-Since: <cached last-modified data>
- 如果内容被修改了, 服务器回送新的文档, 返回 200 状态码和最新的修改日期
- 如果内容没有被修改, 会返回一个
304 Not Modified
响应
If-None-Match / ETag
有些情况下仅使用最后修改日期进行再验证是不够的
- 有些文档有可能会被周期性的重写(比如: 从一个后台进程中写入), 但实际上包含的数据常常是一样分, 尽管内容没有变化, 但修改日期会发生变化
- 有些文档可能被修改了, 但所做修改并不重要. 不需要让世界范围内的缓存都重装数据(比如填写注释)
- 有些服务器无法准确判定其页面的最后修改日期
- 有些服务器提供的文档会在毫秒间隙发生变化(比如, 实时监视器), 对这些服务器来说, 以一秒为粒度的修改日期可能就不够用了
浏览器缓存
当浏览器请求一个网站的时候,会加载各种各样的资源,比如:HTML 文档、图片、CSS 和 JS 等文件。对于一些不经常变的内容,浏览器会将他们保存在本地的文件中,下次访问相同网站的时候,直接加载这些资源,加速访问。
优点:
- 减少页面加载时间;
- 减少服务器负载;
下面介绍几种具体的缓存方案:
DNS 缓存
我们知道 url 其实只是一个别名,真实的服务器请求地址,实际上是一个 IP 地址。获得 IP 地址的方式,就是查询 DNS 映射表。虽然这是一个非常简单的查询,但如果每次用户访问一个 url 都去查询 DNS 一次,未免显得太频繁。DNS 会告诉你,你别老是经常过来,万一我挂了,我们就无法愉快地玩耍了。
DNS 查询过程大约消耗 20 毫秒,在 DNS 查询过程中,浏览器什么都不会做,保持空白。如果 DNS 查询很多,网页性能会受到很大影响,因此需要用到 DNS 缓存。
不同浏览器的缓存机制不同:IE 对 DNS 记录默认的缓存时间为 30 分钟,Firefox 对 DNS 记录默认的缓存时间为 1 分钟,Chrome 对 DNS 记录默认的缓存时间为 1 分钟。
缓存时间长:减少 DNS 的重复查找,节省时间。
缓存时间短:及时检测服务器的 IP 变化,保证访问的正确性。
CDN 缓存
cdn 缓存是一种服务端缓存,CDN 服务商将源站的资源缓存到遍布全国的高性能加速节点上,当用户访问相应的业务资源时,用户会被调度至最接近的节点最近的节点 ip 返回给用户,在 web 性能优化中,它主要起到了,缓解源站压力,优化不同用户的访问速度与体验的作用。
流程
客户端访问网站的过程:没有 CDN:
- 用户在浏览器访问栏中输入要访问的域名;
- 浏览器向 DNS 服务器请求对该域名的解析;
- DNS 服务器返回该域名的 IP 地址给浏览器
- 浏览器使用该 IP 地址向服务器请求内容。
- 服务器将用户请求的内容返回给浏览器。
使用了 CDN:
- 用户在浏览器中输入要访问的域名。
- 浏览器向 DNS 服务器请求对域名进行解析。由于 CDN 对域名解析进行了调整,DNS 服务器会最终将域名的解析权交给 CNAME 指向的 CDN 专用 DNS 服务器。
- CDN 的 DNS 服务器将 CDN 的负载均衡设备 IP 地址返回给用户。
- 用户向 CDN 的负载均衡设备发起内容 URL 访问请求。
- CDN 负载均衡设备会为用户选择一台合适的缓存服务器提供服务。选择的依据包括:根据用户 IP 地址,判断哪一台服务器距离用户最近;根据用户所请求的 URL 中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器的负载情况,判断哪一台服务器的负载较小。基于以上这些依据的综合分析之后,负载均衡设置会把缓存服务器的 IP 地址返回给用户。
- 用户向缓存服务器发出请求。
- 缓存服务器响应用户请求,将用户所需内容传送到用户。如果这台缓存服务器上并没有用户想要的内容,而负载均衡设备依然将它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉取到本地。
缓存规则
- 首次访问,下载网站的静态资源(如:JS、CSS、图片等)到本地
- 第二次访问浏览器从缓存中加载资源,不再请求服务器,提高网站访问速度
- 使用 CDN 当浏览器缓存过期,浏览器不是直接向原站点请求资源,而是想 CDN 最近站点请求
- CDN 最近站点也是有缓存的,如果缓存过期,那么就由 CDN 最近站点向原站点发送请求获取最新资源
CDN 节点缓存机制在不同服务商中是不同的,但一般都遵循 HTTP 协议,通过 http 响应头中的 Cache-Control:max-age 的字段来设置 CDN 节点文件缓存时间。当客户端向 CDN 节点请求数据时,CDN 会判断缓存数据是否过期,若没有过期,则直接将缓存数据返回给客户端,否则就向源站点发出请求,从源站点拉取最新数据,更新本地缓存,并将最新数据返回给客户端。
CDN 缓存时间会对“回源率”产生直接的影响,若 CDN 缓存时间短,则数据经常失效,导致频繁回源,增加了源站的负载,同时也增大了访问延时;若缓存时间长,数据更新时间慢,因此需要针对不同的业务需求来选择特定的数据缓存管理。
比如:
<script type="text/javascript" src="//j1.58cdn.com.cn/escstatic/appSdk/cstSdk/cst-new-app.js?cachevers=30"></script>
<script type="text/javascript" src="//j1.58cdn.com.cn/m58/postnew/util/js/esl_zepto.min_v20150420200700.js"></script>
我们可以给 CDN 加大缓存时间,然后通过版本号来控制引入的前端资源;如果有更新的话,直接更新资源后面拼接的版本号;在浏览器加载资源的时候由于资源 URL 的参数发生了变化,就形成了一个新的资源链接,这是向附近 CDN 站点请求时,是找不到资源的,然后在向原站点请求最新资源更新。