用好浏览器缓存,让网站访问速度起飞

Web项目访问速度优化,有些时候把浏览器缓存用好了,比费劲折腾代码或费钱加服务器,来的效果更直接。下面我们就来梳理下浏览器缓存那些事。

先上一张简单的流程图,看看缓存是怎么发挥作用的:

一个请求发起,浏览器先根据被请求资源的Expires或Cache-Control判断资源的缓存是否过期,未过期直接使用,过期了则向远端发起请求。
远端通过Last-Modified或Etag判断被请求资源是否发生更新,更新了返回新资源,未更新则返回304,不返回新资源,以节省数据传输量。
当浏览器还没有缓存,或缓存过期了,或请求头包含no-cache指令,远端返回新资源。
基本的过程就是这样的,下边是一张详细的流程图:

可以看出,当发起一次http请求,如果有缓存,浏览器会先根据Cache-Control和Expires判断缓存是否过期,如果没有过期则使用缓存而停止请求,浏览器的这个处理过程也被称为强缓存。
当浏览器判定本地缓存过期后向远端发起请求,远端先通过Pragma参数判定是否为强制刷新,如果是,直接返回新资源,不是则通过请求头里的If-None-Match和If-Modified-Since判断浏览器的缓存是否过期,过期返回新资源,未过期则返回http码304,这个过程也被称作协商缓存。
其中If-None-Match和If-Modified-Since,是前一次请求的Response里的Etag和Last-Modified的值。

下边详细了解下影响缓存效果的参数。

Expires

Expires用于设置资源的过期时间。当浏览器请求资源时,服务器会通过"Expires"指定资源的过期时间,使得浏览器可以根据这个时间来决定是否需要重新请求该资源,例如:Expires: Tue, 15 Nov 2021 12:45:26 GMT。不过Expires有一个缺点,就是它依赖于客户端时间,如果客户端时间偏差太大,可能会导致缓存混乱。

Cache-Control

Cache-Control可以包含多种指令,以便Web服务器和客户端之间能够更好地管理缓存。Cache-Control是有http1.1版本里制定的,相对Expires更灵活、可靠。
它用在响应头里,设置缓存的有效期,也可用在请求头里,强制要求远端返回最新的资源。
常用指令:

  • "max-age=秒数":指定在多少秒内可以使用缓存,而不需要再次请求服务器。例如,Cache-Control: max-age=3600表示缓存将在一小时内有效。
  • "no-cache":表示缓存是过期的,必须与服务器确认其有效性。这并不意味着不使用缓存,而是要求客户端与服务器确认缓存的有效性后再使用。
  • "no-store":要求浏览器不使用缓存,每次都必须从源服务器获取资源。
  • "public":表示响应可以被任何缓存存储,包括中间代理服务器。
  • "private":表示响应只能够被单个用户的浏览器缓存,中间代理服务器不能存储该响应。
  • "s-maxage=秒数":类似于"max-age",但仅适用于共享缓存(例如代理服务器)。
    这些指令可以单独使用,也可以组合在一起以实现更精细的缓存控制。

Pragma

Pragma一般用于强制不使用缓存,当浏览器执行强制刷新的时候,请求头里会带上Pragma: no-cacheCache-Control: no-cache两个参数,这时候远端不检查资源是否过期,直接返回最新的资源。

注意:如果响应头里Expires和Cache-Control同时存在,Cache-Control的优先级高于Expires。

Last-Modified

Last-Modified存在于响应头里,它表示该资源的最后修改时间。以UTC格式表示,例如:Last-Modified: Tue, 15 Nov 2021 12:45:26 GMT

If-Modified-Since

当浏览器请求一个资源时,在请求头中加入"If-Modified-Since"字段,将上次缓存的资源的 Last-Modified 发送给服务器。服务器会根据这个时间来判断资源是否发生了变化,例如:If-Modified-Since: Tue, 15 Nov 2021 12:45:26 GMT

Etag

Etag存在于响应头里,它是该资源的唯一标识符,通常是资源内容的哈希值或其他类似的标识,例如:ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

If-None-Match

当浏览器请求一个资源时,在请求头中加入"If-None-Match"字段,将上次缓存的资源的ETag值发送给服务器。服务器会根据这个ETag值来判断资源是否发生了变化,例如:If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

注意:如果请求头里If-Modified-Since和If-None-Match同时存在,If-None-Match的优先级高于If-Modified-Since。