当我们谈论前端性能时,我们想到的是诸如合并、压缩、缓存或服务器上压缩资产之类的操作,以便页面加载更快,用户能够尽快完成目标。
资源预取是另一种提高性能的技术。 我们可以用它来告诉浏览器用户将来可能需要的资产——甚至在他们需要之前。
预取是一种向浏览器暗示将来肯定或可能使用资源的方式,有些暗示适用于当前页面,有些适用于可能的未来页面。
作为开发人员,我们比浏览器更了解我们的应用程序。 我们可以利用此信息告知浏览器有关核心资源的信息。
这种在用户需要之前猜测他们需求的做法被称为预浏览。 但它不仅仅是一种技术,它可以分解为多种不同的技术:dns-prefetch
、
、标准的subresourceprefetch
、preconnect
和prerender
。
DNS 预取
这将通知客户端,我们稍后将需要来自特定 URL 的资产,以便浏览器能够尽快解析 DNS。 假设我们需要来自 URL example.com
的资源,例如图像或音频文件。 在文档的<head>
中,我们将编写
<link rel="dns-prefetch" href="//example.com">
然后,当我们从该 URL 请求文件时,将不再需要等待 DNS 查找。 如果我们使用来自第三方或社交网络的代码,或者从<script>
加载小部件,这将特别有用。
在他的关于前端性能的精彩文章中,Harry Roberts 建议使用这种技术
这一行简单的代码将告诉支持的浏览器在实际需要之前的一小段时间内开始预取该域的 DNS。 这意味着在浏览器命中实际请求小部件的脚本元素时,DNS 查找过程将已在进行中。 这只是让浏览器提前了一小步。
这看起来像是微不足道的性能改进,可能并不重要,但这并非一定如此——Chrome 一直在做类似的事情。 如果你在 URL 栏中输入一个域的小部分,它将自动预解析 DNS(有时甚至会预渲染页面),从而为每个请求节省宝贵的毫秒。
预连接
与 DNS 预取方法类似,预连接将解析 DNS,但它还将进行 TCP 握手和可选的 TLS 协商。 它可以像这样使用
<link rel="preconnect" href="https://css-tricks.org.cn">
有关更多信息,Ilya Grigorik 撰写了一篇关于这种便捷资源提示的精彩文章
现代浏览器尽其所能预判网站在实际请求发出之前将需要哪些连接。 通过尽早启动“预连接”,浏览器可以提前设置必要的套接字,并从实际请求的关键路径中消除代价高昂的 DNS、TCP 和 TLS 往返。
好消息是,我们终于可以帮助浏览器了;我们可以告诉浏览器,在启动实际请求之前,我们将需要哪些套接字,这可以通过在 Firefox 39 和 Chrome 46 中发布的新的预连接提示来实现!
预取
如果我们确定将来会需要特定资源,那么我们可以要求浏览器请求该项目并将其存储在缓存中以供以后参考。 例如图像或脚本,或者任何可以被浏览器缓存的内容
<link rel="prefetch" href="image.png">
与 DNS 预取不同,我们实际上是在请求和下载该资产并将其存储在缓存中。 但是,这取决于许多条件,因为预取可能会被浏览器忽略。 例如,客户可能会放弃在缓慢的网络上请求大型字体文件。 Firefox 只有在“浏览器空闲时”才会预取资源。
正如Bram Stein 在他的文章中解释的那样,这对网络字体来说可能有巨大的性能优势。 目前,字体文件必须等待 DOM 和 CSSOM 建立后才能开始下载。 但是,如果我们预取它们,那么就可以轻松地绕过这一瓶颈。
注意:虽然以前预取资产有点难以测试,但 Chrome 和 Firefox 现在将在 Network 面板中显示预取的资源。 此外,请记住,链接预取没有同源策略限制。
子资源(参见注意)
另一种预取技术有助于识别优先级最高的资源,这些资源应该在预取项目之前请求。 例如,在 Chrome 和 Opera 中,我们可以将以下内容添加到文档的head
中
<link rel="subresource" href="styles.css">
根据 Chromium 文档,它的工作原理如下
“LINK rel=subresource”提供了一种新的链接关系类型,其语义与 LINK rel=prefetch 不同。 虽然 rel=prefetch 提供了对后续页面上将使用资源的低优先级下载,但 rel=subresource 允许尽早加载当前页面内的资源。
所以:如果资产是当前页面所需的,或者如果尽快需要,那么最好使用subresource
,否则坚持使用prefetch
。
预渲染页面
这是终极手段,因为prerender
使我们能够抢先加载某个文档的所有资产,例如
<link rel="prerender" href="https://css-tricks.org.cn">
Steve Souders 撰写了一篇关于这种技术的精彩解释
这就像在一个隐藏的选项卡中打开 URL——所有资源都已下载、DOM 已创建、页面已布局、CSS 已应用、JavaScript 已执行,等等。 如果用户导航到指定的
href
,那么隐藏页面将被切换到视图,使其看起来是立即加载的。 Google 搜索多年来一直使用名为即时页面的功能。 微软最近宣布他们将在 IE11 上的必应中以类似的方式使用预渲染。
但要注意!你应该确定用户会点击该链接,否则客户端将毫无理由地下载渲染页面所需的所有资产。
Souders 继续说道
与所有这些预先工作一样,存在预测错误的风险。 如果预先工作很昂贵(例如,从其他进程中窃取 CPU、消耗电池或浪费带宽),那么就需要谨慎。 似乎很难预测用户接下来会访问哪个页面,但确实存在高置信度场景
- 如果用户进行了一次搜索,并有明显的结果,那么该结果页面很可能在下一步加载。
- 如果用户导航到登录页面,那么登录后的页面很可能在下一步加载。
- 如果用户正在阅读多页文章或分页结果集,那么当前页面后的页面很可能在下一步加载。
最后,页面可见性 API可用于防止脚本在它们在用户屏幕上呈现之前执行。
好的,现在我们已经排除了这些设计考虑因素,我们可以谈谈规范中可能令人感兴趣的未来添加内容。
未来选项:预加载
名为preload的新规范建议,有时最好始终下载资产,而不管浏览器是否认为这是一个好主意。 与可以被忽略的预取资产不同,预加载资产必须被浏览器请求。
<link rel="preload" href="image.png">
所以,虽然预加载目前不受任何浏览器的支持,但它背后的理念确实很有趣。
总结
预测用户接下来会做什么很困难,这肯定需要大量的计划和测试。 但是,性能优势绝对值得追求。 如果我们愿意尝试这些预取技术,那么我们一定会以明显的方式改善用户体验。
终于有了关于这个的一些快速且最新的文档。 谢谢!
事实是,一段时间前 我问过类似的事情,但我认为用的是“不合适的”术语。
简单地说,假设我的网站有一个“加载中...”图像,可能还有一个背景平铺图像,没什么花哨的。
有没有办法告诉“嘿,在**并行请求的前 6 个位置**下载这些东西”?
很棒的文章 Robin。 谢谢。
不幸的是,
<link rel="subresource"…
并没有像你想象的那么有用...在 Chrome 中,以这种方式暗示的子资源默认情况下会获得非常低的下载优先级 (https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp&q=LinkSubresource&sq=package:chromium&type=cs&l=122)
当浏览器通过正常方式发现它们时,它们应该会得到适当的重新优先级。
当预加载完全支持时,
rel=subresource
的需求应该会消失。我认为
prerender
和subresource
非常棒。 我更愿意只实现它们,因为我认为它们不会让我过于担心。我真的很喜欢这个! 我一直在寻找其他东西来帮助提高网站性能。
我们大多数人可能以前都听说过这些属性。 问题是采用率以及我们是否可以使用它们。 如果在每个浏览器中都可用,那么详细的列表将是本文的关键。 谢谢你的写作!
下表提供浏览器支持信息
https://en.wikipedia.org/wiki/Link_prefetching#Browser_support
我们大多数人可能都听说过 https://caniuse.cn Ben。 哈哈
哈哈,没错! 这些东西几乎没有得到支持。 任何关注规范更新的人都ihavenolife都知道这些存在。 支持是关键问题。
以前不知道这些东西,真是太好了。 我猜如果使用了
prerender
,服务器在点击链接时必须返回 304? 还是它根本不检查服务器?你没有提到
preconnect
,它在 Chrome 46 和 Firefox 39 中可用 - https://www.igvita.com/2015/08/17/eliminating-roundtrips-with-preconnect/谢谢,Joseph! 我刚用那个资源提示更新了帖子。
@Joseph Scott
+1!
我注意到 DNS 预取是协议相关的 (
//example.com
)。 有没有特定的原因,考虑到这一点?我编写了一个小的 JavaScript 实用程序来帮助使用 preconnect、prefetch 和 prerender:https://github.com/schliflo/BoostR
所以,一方面,我在这里了解到,我们可以使用 HTML 声明来预先处理这个和那个,但另一方面,我想说,我们仍然认为应该继续使用脚本。
浏览器在使用 HTML 预处理时做出任意决定的明显事实是有道理的。 浏览器和脚本之间存在某种契约,这一点也是有道理的,但是我的理解是,浏览器仍然是仲裁者,而脚本只是通过浏览器的脚本引擎发出请求,而脚本引擎会将其 API 公开给脚本。
我已经实现了只使用 HTML,并在现代浏览器中看到了立竿见影的效果。 如果我的理解是正确的,而我(我们)仍然鼓励也使用脚本,那么优先级是如何管理的,由什么管理?
浏览器仍然是仲裁者,但是它的两个孩子哪个更受青睐? 当浏览器收到可能存在矛盾的预处理命令时,可能会发生什么? W3C 没有告诉我们关于使用 Link 类型时的区别。
这太棒了。 谢谢分享。
这些很棒,但是对 Google Analytics 和 prerender 之类的东西有什么影响? 这算作一次点击吗?