原文链接:Love your cache ❤️
译者:Yodonicc
用户在第二次加载你的网站时会使用他们的HTTP缓存,所以要确保它运行良好。
这篇文章是”爱上HTTP缓存”视频的配套文章,是Chrome Dev Summit 2020的扩展内容的一部分。请务必查看该视频。
当用户第二次加载您的网站时,他们的浏览器会使用其HTTP缓存内的资源,以帮助提高加载速度。但是,网络上的缓存标准可以追溯到1999年,而且定义相当宽泛–确定一个文件(如CSS或图片)是否可以再次从网络上获取,还是从缓存中加载,是一门不精确的科学。
在这篇文章中,我将讲述一个合理的、现代的默认缓存–实际上根本就没有缓存。但这只是默认情况,当然,这比 “仅仅只是不使用缓存”更有意义。继续阅读!
在建立你的网站时需要记住的是,像Core Web Vitals这样的性能指标包括所有的加载,而不仅仅是第一次加载。然而,谷歌的很多指导都集中在优化首次加载(这对吸引用户绝对是很重要的!),而Lighthouse只在空缓存的情况下测试你的网站。
目标
当一个网站第二次被加载时,你有两个目标。
- 确保你的用户得到最新的版本–如果你改变了什么,应该迅速反映出来。
- 做到#1,同时尽可能少地从网络中获取信息
在最广泛的意义上,你只想在你的客户再次加载你的网站时向他们发送最小的变化。而构建你的网站以确保最有效地分配任何变化是具有挑战性的(下文和上方视频中有更多关于这个问题的内容)。
说到这里,当你考虑缓存时,你还有其他的解决办法——也许你已经决定让用户的浏览器HTTP缓存在你的网站上保留很长一段时间,这样就不需要网络请求来提供服务。或者你已经构建了一个服务工作者,在检查网站是否是最新的之前完全离线提供服务。这是一个极端的选择,它是有效的,并被用于许多类似于离线应用程序的网络体验,但浏览器不需要处于一个只有缓存的极端,甚至是一个完全只有网络的极端。
背景
作为网络开发者,我们都习惯了有一个 “陈旧的缓存”的想法。但我们几乎本能地知道有哪些工具可以解决这个问题:做一个 “硬刷新”,或者打开一个隐身窗口,或者使用浏览器的一些组合的开发工具来清除网站的数据。
互联网上的普通用户则没有这样的奢侈。因此,尽管我们有一些核心目标,即确保我们的用户在第二次加载时有一个很好的时间,但确保他们不会有一个糟糕的时间或被卡住也非常重要。(如果你想听我讲讲我们是如何让web.dev/live网站卡住的,请看视频!)。
说一下背景,”陈旧的缓存”的一个真正常见的原因实际上是1999年时代的默认缓存。它依赖于Last-Modified
标头。
图中显示了不同的资源被用户的浏览器缓存了多长时间
在不同时间产生的资产(灰色)将被缓存不同的时间,所以第二次加载可以得到缓存和新鲜资产的组合。你加载的每一个文件都会在其当前寿命的基础上再保留10%,因为你的浏览器会看到它。例如,如果index.html
是一个月前创建的,它将被你的浏览器缓存大约三天。
这在过去是一个善意的想法,但考虑到今天网站的紧密集成性,这种默认行为意味着有可能进入这样一种状态:用户拥有为你的网站的不同版本设计的文件(例如,周二发布的JS和周五发布的CSS),都是因为这些文件没有完全在同一时间更新。
希望之路
现代默认的缓存方式是根本不做缓存,而是使用CDN将你的内容带到用户身边。每次用户加载你的网站时,他们都会去网络上看看是否是最新的内容。这个请求将具有低延迟,因为它将由地理上靠近每个最终用户的CDN提供。
你可以配置你的网络主机,让它用这个头来响应网络请求。
Cache-Control: max-age=0,must-revalidate,public
这基本上是说;该文件在任何时候都是必须重新验证的的,你必须从网络上验证它才能再次使用它(否则它只是 “建议”)。
你也可以指定no-cache
,而不是max-age=0,must-revalidate
:这是等同的。然而,no-cache
是一个令人困惑的名字,因为它可以被解释为 “永远不缓存这个文件”–尽管事实并非如此。想了解更多内容,请看MDN上的Cache-Control。
这个验证过程在传输的字节数上是相对容易的–如果一个大的图像文件没有变化,你的浏览器会收到一个小的304响应–但是它要付出延迟的代价,因为用户仍然必须去网络上寻找答案。这就是这种方法的主要缺点。对于第一世界的快速连接的人来说,它可以很好地工作,而且你选择的CDN有很大的覆盖面,但对于那些可能使用较慢的移动连接或使用较差的基础设施的人来说,就不是这样了。
不管怎么说,这是一种现代的方法,在一个流行的CDN——Netlify上是默认的,但几乎可以在任何CDN上进行配置。对于Firebase主机,你可以在firebase.json文件的主机部分包含这个头。
"headers": [
// Be sure to put this last, to not override other headers
{
"source": "**",
"headers": [ {
"key": "Cache-Control",
"value": "max-age=0,must-revalidate,public"
}
}
]
因此,虽然我仍然建议这是一个明智的默认值,但它只是——默认值而已。请继续阅读,了解如何介入并升级默认处理方式。
指纹URLs
通过在网站上提供的资源、图片等的名称中包含文件内容的哈希值,你可以确保这些文件总是有唯一的内容–这将导致文件被命名为sitecode.af12de.js
,比如说。当你的服务器响应对这些文件的请求时,你可以安全地指示你的终端用户的浏览器通过配置这个头来长时间地缓存这些文件。
Cache-Control: max-age=31536000,immutable
这个值是一年,单位是秒。而根据规范,这实际上等于 “永远”。
重要的是,不要用手来生成这些哈希值–那是太多的手工工作了!你可以使用Webpack等工具。你可以使用Webpack、Rollup等工具来帮助你完成这项工作。请务必在Tooling Report上阅读更多关于它们的信息。
记住,不仅仅是JavaScript可以从指纹URL中受益;像图标、CSS和其他不可变的数据文件等资产也可以用这种方式命名。(一定要看上面的视频,以了解更多关于代码分割的信息,它可以让你在网站变化时运送更少的代码)。
我们在上面的
Cache-Control
建议中加入了关键字immutable
。如果没有这个关键词,我们的长篇Cache-Control
只被认为是一个建议,一些浏览器仍然会忽略它而去找服务器。(在2017年,Chrome浏览器改变了它的行为,所以无论如何,它总是表现得好像不可更改的关键字是开着的–所以现在,只有Safari和Firefox需要它)。
无论你的网站如何处理缓存,这些指纹文件对你可能建立的任何网站都是非常宝贵的。大多数网站只是没有在每个版本中改变。
当然,我们不能以这种方式重命名我们的友好、面向用户的页面:将你的index.html文件重命名为index.abcd12.html
——这是不可行的,你不能告诉用户每次加载你的网站时都要去一个新的URL。这些 “友好 “的URL不能以这种方式重命名和缓存,这使我想到了一个可能的中间方案。
中间方案
当涉及到缓存时,显然有一个中间地带的空间。我提出了两个极端的选择:永远不缓存,或者永远缓存。有一些文件你可能想缓存一段时间,比如我上面提到的 “友好 “URLs。
如果你确实想缓存这些 “友好 “的URL和它们的HTML,那么值得考虑的是它们包括哪些依赖关系,它们如何被缓存,以及在一段时间内缓存它们的URL会对你有什么影响。让我们来看看一个HTML页面,其中包括一个像这样的图片。
<img src="/images/foo.jpeg" loading="lazy" />
如果你更新或改变你的网站,删除或改变这个懒惰加载的图像,查看你的HTML缓存版本的用户可能会得到一个不正确或缺失的图像–因为当他们重新访问你的网站时,他们仍然缓存了原始的/images/foo.jpeg
。
如果你很小心,这可能不会影响你。但广泛而言,重要的是要记住,你的网站–当被你的终端用户缓存时——不再仅仅存在于你的服务器上。相反,它可能以碎片形式存在于你的终端用户的浏览器的缓存中。
一般来说,大多数关于缓存的指南都会提到这种设置——你想缓存一个小时,几个小时,等等。要设置这样的缓存,可以使用这样的标题(缓存时间为3600秒,即一个小时)。
Cache-Control: max-age=3600,immutable,public
最后一点,如果你创建的是及时的内容,而这些内容通常只能被用户访问一次,比如新闻文章!我的看法是,这些内容永远都不应该被缓存,你应该使用我们上面的合理的默认值。我认为我们常常高估了缓存的价值,而忽略了用户希望总是看到最新、最棒的内容的愿望,例如对一个新闻故事或当前事件的重要更新。
非HTML选项
除了HTML之外,其他一些生活在中间地带的文件的选择包括。
- 一般来说,寻找不影响其他部分的资源
- 例如:避免缓存CSS,因为它会导致你的HTML呈现方式的改变
- 作为及时文章一部分的大型图片
- 你的用户可能不会访问任何一篇文章超过几次,所以不要永远缓存照片或大型图像,以免浪费存储空间。
- 代表某样有有效期的资源
- JSON数据可能每小时才发布一次,所以你可以把之前的结果缓存一个小时——它不会在你的窗口中改变。
- 开源项目的构建可能是有速度限制的,所以可以缓存构建状态的图片,直到状态有可能发生变化。
总结
当用户第二次加载你的网站时,你已经得到了一张信任票——他们想再回来,并获得你所提供的更多东西。在这一点上,并不总是要把加载时间降低,你有很多选择,可以确保你的浏览器只做它需要的工作,以提供快速和最新的体验。
缓存在网络上并不是一个新的概念,但也许它需要一个合理的默认值——考虑使用一个默认值,并在你需要时强烈选择使用更好的缓存策略。谢谢你的阅读!
也请期待本系列下一篇文章
关于HTTP缓存的一般指南,请期待本系列下一篇文章《使用HTTP缓存防止不必要的网络请求》。
注:特别感谢技术指导dazhao(赵达)对本文翻译的审阅指正。