rel=prefetch 资源提示及其使用方法详解
发布日期:2019年9月12日,最后更新日期:2025年2月8日
研究表明,页面加载时间越短,转化率就越高,用户体验也越好。如果我们了解用户在网站中的浏览路径以及他们接下来可能访问的页面,就可以通过预先下载这些页面的资源,显著减少后续导航的加载时间。
本指南将介绍如何使用 rel=prefetch 高效地实现预取,从而达成这一目标。
使用 rel=prefetch 提升网站性能
在网页中添加 rel=prefetch 可以指示浏览器提前下载用户未来可能需要的资源,例如脚本或CSS文件。
预取提示会消耗额外带宽来加载非立即需要的资源,因此这一技术需要谨慎使用。请确保只预取用户确实需要的资源。如果用户使用的是低速连接,建议避免预取,这可以通过 Network Information API 进行检测。
使用场景
预取技术通过提前下载资源来提升网页速度,存在多种应用场景。
预取后续页面
如果后续页面是可预测的,可以预取HTML文档,使得用户点击链接时页面能够立即加载。
注意:对于支持 Speculation Rules API 的浏览器,使用该API进行导航预取相比简单的 rel=prefetch 具有一些优势。例如,它能够处理不可缓存的导航,并始终支持跨源预取。
例如,在商品列表页面,可以预取列表中热门商品的详情页。在某些场景下,预测下一次导航则更为直接。例如在购物车页面,用户极有可能前往结算页面,这使其成为理想的预取候选。
注:eBay 在其搜索结果页的前五个结果中实施了预取,从而加速了后续页面的加载,这对转化率产生了积极影响。
尽管资源预取会消耗额外的带宽,但它能改善大多数性能指标。当文档请求命中缓存时,首字节时间(TTFB)会显著缩短。TTFB的减少通常也会带动后续基于时间的指标改善,例如 Largest Contentful Paint(LCP)和 First Contentful Paint(FCP)。
预取静态资源
如果能预测用户可能访问的后续板块,可以预取脚本、样式表等静态资源。这在资源被多个页面共享时尤其有用。
例如,Netflix 利用用户在注销页面的停留时间,预取用户登录后需要用到的 React 资源。这使后续导航的 Time to Interactive(可交互时间)缩短了30%。
截至本文撰写时,预取资源可以在不同源之间跨页面共享。然而,随着“双密钥HTTP缓存”(Double-key HTTP Cache)机制的推出,这一特性将仅限于顶级导航和同源子资源。这意味着,即使 a.com 预取了资源 b.com/library.js,c.com 也无法使用该缓存。此外,某些基于 WebKit 的浏览器已经对所有第三方域的缓存和HTML5存储进行了分区。
静态资源预取对性能指标的影响取决于具体预取的资源类型:
- 预取图片:可以显著缩短作为 LCP 元素的图片的 LCP 时间。
- 预取样式表:由于无需等待样式表下载,可以同时改善 FCP 和 LCP。样式表会阻塞渲染,预取它们有助于降低 LCP。如果下一页的 LCP 元素是使用
background-image属性请求的CSS背景图片,该图片也会作为预加载样式表的依赖资源一同被预取。 - 预取 JavaScript:相比在导航时才从网络获取,预取的脚本可以更早地被处理。这可能会对页面的 Interaction to Next Paint(INP)指标产生影响。如果标记是使用 JavaScript 在客户端渲染的,通过减少资源加载延迟可以改善 LCP,并能更早地渲染包含 LCP 元素的标记。
- 预取网页字体:预取当前页面尚未使用但后续页面需要的网页字体,可以避免布局偏移。如果使用了
font-display: swap;,预取可以消除字体交换期,从而加快文本渲染速度并消除布局偏移。如果预取的字体在后续页面中被使用,并且该页面的 LCP 元素是使用了该网页字体的文本块,那么该元素的 LCP 也会更快。
预取按需加载的 JavaScript 代码块
通过代码分割将 JavaScript 打包成多个块,可以仅加载应用的一部分,其余部分按需懒加载。采用此技术时,可以将预取应用于那些并非立即需要,但在不久的将来很可能被请求的路由或组件。
例如,一个包含打开表情选择器对话框按钮的页面,可以将代码分割为三个 JavaScript 块:首页、对话框、选择器。首页和对话框可以优先加载,而选择器则按需加载。使用 webpack 等工具,可以指示浏览器预取这些按需加载的代码块。
rel=prefetch 的实现方法
实现预取最简单的方法是在文档的 <head> 中添加 <link> 标签。
<head>
...
<link rel="prefetch" href="/next-page.html" as="document">
...
</head>
也可以使用 Link HTTP 头来启动预取:
Link: </next-page.html>; rel=prefetch
通过HTTP头指定预取提示,有时能带来轻微的性能提升,因为浏览器无需解析文档来寻找资源提示。
注意:除 Safari 外,所有主流现代浏览器均支持 prefetch。对于 Safari,可以使用 XHR 请求或 Fetch API 作为备选方案。
使用 Webpack 魔术注释预取 JavaScript 模块
Webpack 允许您预取用户很可能马上访问或使用的路由或功能的脚本。
以下代码片段演示了如何从 lodash 库懒加载一个排序函数,用于对表单提交的一系列数字进行排序。
form.addEventListener("submit", e => {
e.preventDefault()
import('lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
与其等待“提交”事件触发后才加载该函数,不如预取此资源,增加其在用户提交表单时已存在于缓存的可能性。在 webpack 中,可以通过在 import() 中使用魔术注释来实现:
form.addEventListener("submit", e => {
e.preventDefault()
import(/* webpackPrefetch: true */ 'lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
这将指示 webpack 在 HTML 文档中插入 <link rel="prefetch"> 标签。
预取按需代码块带来的性能好处可能比较微妙,但通常能确保按需代码块更快就绪,从而加快依赖于这些代码块的交互响应速度。根据交互性质的不同,这可能对页面的 INP 指标产生积极影响。
通常,预取会影响资源的整体优先级。预取资源以尽可能低的优先级执行,因此不会与当前页面所需资源竞争带宽。
使用 quicklink 和 Guess.js 实现智能预取
您也可以使用内部实现了预取功能的库来构建更智能的预取策略。
- quicklink:使用 Intersection Observer API 检测链接何时进入视口,并在浏览器空闲时预取链接资源。额外优势:quicklink 的体积小于 1 KB。
- Guess.js:利用分析报告构建预测模型,智能地预取用户最可能需要的内容。
quicklink 和 Guess.js 都使用了 Network Information API,在检测到用户处于低速网络或开启 Save-Data 模式时会自动避免预取。
预取的工作原理
资源提示并非强制性指令,浏览器有权决定是否执行以及何时执行。
同一页面可以多次使用预取。浏览器会将所有提示加入队列,并在空闲时请求各个资源。在 Chrome 中,如果预取加载尚未完成,而用户已经导航到目标预取资源,浏览器会将正在进行的加载视为导航(其他浏览器厂商的实现可能有所不同)。
预取以“低”优先级进行,因此预取的资源不会与当前页面所需的资源竞争带宽。
预取的文件如果可缓存,会被存储于 HTTP 缓存中。否则,它们将被丢弃且不会被使用。
总结
使用 rel=prefetch 预取未来需要的资源,可以显著改善网站的 Web 性能。该特性已获得大多数现代浏览器的支持。但由于此技术涉及加载可能用不到的额外字节,使用时需保持谨慎。建议仅在必要时使用,并且最好只在高速网络环境下启用。
正在加载评论...