通过网络获取资源既缓慢又昂贵:
-
- 大型响应需要浏览器和服务器之间的多次往返。
- 直到所有关键资源都完全下载后,您的页面才会加载。
- 如果一个人使用有限的移动数据计划访问您的网站,那么每个不必要的网络请求都是在浪费他们的钱。
如何避免不必要的网络请求? 浏览器的 HTTP 缓存是您的第一道防线。 它不一定是最强大或最灵活的方法,它对缓存响应的生命周期的控制有限,但它是有效的,所有浏览器都支持它,并且不需要太多工作。
实际上没有一个称为 HTTP 缓存的 API。 它是 Web 平台 API 集合的总称。 所有浏览器都支持这些 API:
-
- Cache-Control
- ETag
- Last-Modified
HTTP cache 的工作原理
浏览器发出的所有 HTTP 请求都首先路由到浏览器缓存,以检查是否存在可用于满足请求的有效缓存响应。如果匹配,则从缓存中读取响应,从而消除网络延迟和传输产生的数据成本。
HTTP 缓存的行为由请求标头和响应标头的组合控制。在理想情况下,您可以控制 Web 应用程序的代码(将确定请求标头即 HTTP request headers)和 Web 服务器的配置(将确定响应标头即 HTTP response headers)。
Request headers: stick with the defaults (usually)
虽然有许多重要的标头应该包含在您的 Web 应用程序的传出请求中,但浏览器在发出请求时几乎总是代表您进行设置。 影响检查新鲜度的请求标头,如 If-None-Match 和 If-Modified-Since 只是基于浏览器对 HTTP 缓存中当前值的理解而出现。
这是个好消息——这意味着您可以继续在 HTML 中包含诸如下列例子之类的标签,并且浏览器会自动为您处理 HTTP 缓存,而无需额外的努力。
<img src="my-image.png">
Response headers: configure your web server
HTTP 缓存设置中最重要的部分是您的 Web 服务器添加到每个传出响应的标头。 以下标头都会影响有效的缓存行为:
-
- Cache-control: 服务器可以返回一个 Cache-Control 指令来指定浏览器和其他中间缓存应该如何缓存单个响应以及缓存多长时间。
- ETAG: 当浏览器发现过期的缓存响应时,它可以向服务器发送一个小令牌(通常是文件内容的哈希)以检查文件是否已更改。 如果服务器返回相同的令牌,则文件是相同的,无需重新下载。
- Last-Modified: 此标头与 ETag 的用途相同,但使用基于时间的策略来确定资源是否已更改,这与 ETag 的基于内容的策略相反。
默认情况下,某些 Web 服务器内置支持设置这些标头,而其他 Web 服务器则完全保留标头,除非您明确配置它们。 如何配置标头的具体细节因您使用的 Web 服务器而异,您应该查阅服务器的文档以获得最准确的详细信息。
省略 Cache-Control 响应标头不会禁用 HTTP 缓存! 相反,浏览器有效地猜测哪种类型的缓存行为对给定类型的内容最有意义。 您可能需要更多的控制权,因此请花时间配置您的响应标头。
在配置 Web 服务器的响应标头时,您应该涵盖两个重要的场景。
Long-lived caching for versioned URLs
假设您的服务器指示浏览器将 CSS 文件缓存 1 年(Cache-Control: max-age=31536000),但您的设计人员刚刚进行了一个需要立即推出的紧急更新。您如何通知浏览器更新文件的“陈旧”缓存副本?你不能,至少不能不改变资源的 URL。在浏览器缓存响应后,缓存版本将一直使用,直到它不再是最新的,这由 max-age 或 expires 决定,或者直到它因某些其他原因从缓存中被驱逐——例如,用户清除他们的浏览器缓存。因此,在构建页面时,不同的用户最终可能会使用不同版本的文件:刚刚获取资源的用户使用新版本,而缓存较早(但仍然有效)副本的用户使用其旧版本回复。您如何获得两全其美:客户端缓存和快速更新?您更改资源的 URL 并强制用户在其内容更改时下载新响应。通常,您可以通过在文件名中嵌入文件的指纹或版本号来实现这一点,例如 style.x234dff.css。
当响应包含“指纹”或版本信息且其内容永远不会改变的 URL 请求时,请将 Cache-Control: max-age=31536000 添加到您的响应中。
设置这个值告诉浏览器,当它需要在接下来的一年内的任何时候(31,536,000 秒;支持的最大值)加载相同的 URL 时,它可以立即使用 HTTP 缓存中的值,而无需向网络发出请求 你的网络服务器。 太好了 - 您立即获得了避免网络带来的可靠性和速度!
像 webpack 这样的构建工具可以自动化将哈希指纹分配给资产 URL 的过程。
Server revalidation for unversioned URLs
不幸的是,并非您加载的所有 URL 都是版本化的。
也许您无法在部署 Web 应用程序之前包含构建步骤,因此您无法向资产 URL 添加哈希值。 并且每个 Web 应用程序都需要 HTML 文件——这些文件(几乎!)永远不会包含版本信息,因为如果他们需要记住要访问的 URL 是 https://,那么没有人会费心使用您的 Web 应用程序 /index.34def12.html。 那么你能对这些 URL 做些什么呢?
这是您需要承认失败的一种情况。 单独的 HTTP 缓存不足以完全避开网络 request. 但是你可以采取一些步骤来确保网络请求尽可能快, 并尽可能高效。
以下 Cache-Control 值可以帮助您微调未版本控制的 URL 的缓存位置和方式:
-
- no-cache: 这会指示浏览器每次使用 URL 的缓存版本之前都必须与服务器重新验证。
- no-store: 这会指示浏览器和其他中间缓存(如 CDN)从不存储文件的任何版本。
- private: 浏览器可以缓存文件,但中间缓存不能。
- public: 响应可以由任何缓存存储。
Etag 使用例子
浏览器从服务器请求 /file 并包含 If-None-Match 标头以指示服务器仅在服务器上文件的 ETag 与浏览器的 If-None-Match 值不匹配时才返回完整文件。 在这种情况下,这 2 个值确实匹配,因此服务器返回 304 Not Modified 响应,并说明文件应缓存多长时间(Cache-Control: max-age=120)。
总结
HTTP Cache 是一种提高负载性能的有效方式,因为它减少了不必要的网络请求。 它在所有浏览器中都受支持,并且不需要太多设置。
以下 Cache-Control 配置是一个好的开始:
-
- Cache-control: no-cache - 对于每次使用前应与服务器重新验证的资源
- Cache-control:no-store: - 不应该缓存的资源
- Cache-control: max-age=31536000:版本化资源