关于prefetch和preload

在 Web 开发中,我们经常需要加载各种资源,如图片、字体、样式表、脚本等,以提高用户体验和功能性。然而,这些资源的加载顺序和时机对于应用的性能和用户感知有很大的影响。如果我们能够提前告诉浏览器哪些资源是重要的,或者哪些资源是将来可能需要的,那么我们就可以更好地利用浏览器的资源预取能力,从而加速页面的渲染和交互。

为了实现这个目标,HTML5 提供了两个非常有用的标签:<link rel="preload"><link rel="prefetch">。它们可以让我们在 HTML 文档中指定哪些资源需要被预加载或预获取,以及它们的类型、优先级和时间点。本文将介绍这两个标签的一些技术要点。

基本用法

preload

<link rel="preload"> 标签可以让我们告诉浏览器哪些资源是当前页面渲染或执行所必需的,或者是很快就会被用到的。这样,浏览器可以在解析 HTML 的同时,提前请求这些资源,并将它们存放在缓存中,以便后续使用。这样可以避免一些延迟加载导致的闪烁、卡顿或空白等问题。

例如,如果我们有一个字体文件 font.woff2,它是页面中文本显示所依赖的,那么我们可以在 HTML 中添加如下标签:

<link rel="preload" as="font" type="font/woff2" href="font.woff2" crossorigin />

这样,浏览器会尽早地请求这个字体文件,并将其缓存起来,等到 CSS 中引用了这个字体时,就可以直接使用,而不需要再等待网络请求。注意,这里需要指定 as 属性为 font,以告诉浏览器这是一个字体文件,从而使用正确的请求优先级和缓存策略。另外,由于字体文件通常是跨域的,所以还需要添加 crossorigin 属性,以避免出现跨域错误。

类似地,我们还可以使用 preload 标签来预加载图片、视频、音频、样式表、脚本等资源。只要指定正确的 as 属性和 type 属性(如果有必要),就可以让浏览器更智能地处理这些资源。例如:

<link rel="preload" as="image" href="banner.jpg" />
<link rel="preload" as="style" href="style.css" />
<link rel="preload" as="script" href="script.js" />

prefetch

<link rel="prefetch"> 标签可以让我们告诉浏览器哪些资源是将来可能会被用到的,但不一定是当前页面所必需的。这样,浏览器可以在空闲时间(例如当前页面已经加载完毕后)去请求这些资源,并将它们存放在缓存中,以便后续使用。这样可以提高后续页面或操作的响应速度和用户体验。

例如,如果我们有一个网站,其中有一个登录页面 login.html 和一个主页 index.html。假设大部分用户在登录后会访问主页,那么我们可以在登录页面中添加如下标签:

<link rel="prefetch" href="index.html" />

这样,浏览器会在用户登录成功后,利用空闲时间去请求主页的 HTML 文件,并将其缓存起来。当用户点击跳转到主页时,就可以直接从缓存中读取,而不需要再等待网络请求。达到提高用户的使用体验的目的。

类似地,我们还可以使用 prefetch 标签来预获取图片、视频、音频、样式表、脚本等资源。只要指定正确的 href 属性,就可以让浏览器在合适的时机去请求这些资源。例如:

<link rel="prefetch" href="video.mp4" />
<link rel="prefetch" href="style2.css" />
<link rel="prefetch" href="script2.js" />

更多技术要点

什么情况会导致二次获取?

虽然 preloadprefetch 标签都可以让浏览器提前请求并缓存资源,但有些情况下,浏览器可能会再次获取同一个资源,导致性能损失或带宽浪费。这些情况包括:

  • 资源的 URL 不一致。例如,如果我们使用 <link rel="preload" href="/style.css"> 预加载了一个样式表,但是在 CSS 中使用了相对路径 @import url("style.css") 引用了同一个样式表,那么浏览器可能会认为这是两个不同的资源,从而再次发起请求。为了避免这种情况,我们应该保持资源的 URL 一致,或者使用绝对路径。
  • 资源的属性不一致。例如,如果我们使用 <link rel="preload" as="image" href="banner.jpg"> 预加载了一个图片,但是在 HTML 中使用了 <img src="banner.jpg" width="300" height="200"> 显示了同一个图片,那么浏览器可能会认为这是两个不同的资源,从而再次发起请求。为了避免这种情况,我们应该保持资源的属性一致,或者使用响应式图片技术。
  • 资源的缓存策略不一致。例如,如果我们使用 <link rel="preload" href="/script.js"> 预加载了一个脚本,但是这个脚本的响应头中设置了 Cache-Control: no-cache,那么浏览器可能会认为这个资源不能被缓存,从而再次发起请求。为了避免这种情况,我们应该保持资源的缓存策略一致,或者使用合理的缓存控制机制。

会引起带宽的浪费吗?

使用 preloadprefetch 标签可以让我们更好地控制资源的加载时机和优先级,从而提高应用的性能和用户体验。然而,这也可能会带来一些副作用,例如:

  • 预加载或预获取过多或过早的资源可能会占用用户的带宽和流量,从而影响当前页面或其它页面的加载速度和稳定性。
  • 预加载或预获取不必要或不相关的资源可能会造成用户的带宽和流量的浪费,从而增加用户的成本和不满。
  • 预加载或预获取敏感或隐私的资源可能会暴露用户的行为或偏好,从而引起用户的担忧或不安。

为了避免这些副作用,我们应该谨慎地使用 preloadprefetch 标签,遵循以下原则:

  • 只预加载或预获取当前页面或后续页面所必需或高度相关的资源,避免预加载或预获取过多或过早的资源。
  • 根据资源的类型和重要性,合理地设置资源的请求优先级和缓存策略,避免预加载或预获取不必要或不相关的资源。
  • 尊重用户的意愿和隐私,避免预加载或预获取敏感或隐私的资源,或者提供用户关闭预加载或预获取功能的选项。

如何检测 preload 支持情况?

虽然 preload 标签已经被大多数现代浏览器所支持,但是仍然有一些浏览器不支持或部分支持这个特性。为了保证应用的兼容性和稳定性,我们应该在使用 preload 标签之前,检测浏览器是否支持这个特性,以及是否支持特定类型的资源。

一种简单的检测方法是使用 relList 属性,它可以返回一个 DOMTokenList 对象,表示元素上所有可用的 rel 值。我们可以通过判断 relList 是否包含 preload 来判断浏览器是否支持 preload 特性。例如:

const link = document.createElement('link');
if (link.relList && link.relList.supports && link.relList.supports('preload')) {
  // 浏览器支持 preload
} else {
  // 浏览器不支持 preload
}

另一种检测方法是使用 loadCSS 库,它可以提供一个 onloadCSS 函数,用于检测样式表是否被加载完成。我们可以通过给样式表添加一个 onload 属性,来判断浏览器是否支持 preload 特性。例如:

<link rel="preload" as="style" href="style.css" onload="this.rel='stylesheet'" />
<script src="loadCSS.js"></script>
<script>
  onloadCSS(document.querySelector('link[rel=preload]'), function () {
    // 浏览器支持 preload
  });
</script>

如果浏览器不支持 preload 特性,那么样式表将不会被加载,也不会触发 onload 事件。为了解决这个问题,我们可以使用一个回退方案,即在 noscript 标签中再次引用样式表,并设置 rel 属性为 stylesheet。例如:

<link rel="preload" as="style" href="style.css" onload="this.rel='stylesheet'" />
<noscript><link rel="stylesheet" href="style.css" /></noscript>
<script src="loadCSS.js"></script>
<script>
  onloadCSS(document.querySelector('link[rel=preload]'), function () {
    // 浏览器支持 preload
  });
</script>

这样,如果浏览器不支持 preload 特性,或者用户禁用了 JavaScript,那么样式表仍然可以被正常加载。

不同资源浏览器优先级

使用 preloadprefetch 标签可以让我们更好地控制资源的加载时机和优先级,但是不同类型的资源在不同浏览器中可能有不同的优先级。为了更好地理解和利用这些优先级,我们可以参考以下表格,它显示了不同类型的资源在不同浏览器中的默认优先级(高、中、低),以及使用 preloadprefetch 标签后的优先级变化(↑、↓、=)。注意,这些优先级可能会随着浏览器的版本和设置而变化,所以只能作为一个参考,而不是一个准则。

资源类型ChromeFirefoxSafariEdge
HTML
CSS
JS
preload JS高 ↑高 ↑高 ↑高 ↑
prefetch JS低 ↓低 ↓低 ↓低 ↓
Font
preload Font高 ↑高 ↑高 ↑高 ↑
prefetch Font低 ↓低 ↓低 ↓低 ↓
Image
preload Image高 ↑高 ↑高 ↑高 ↑
prefetch Image低=低=低=低=
Video
preload Video中 ↑中 ↑中 ↑中 ↑
prefetch Video低=低=低=低=

从表格中我们可以看出,一般来说,HTML 和 CSS 是最高优先级的资源,因为它们是页面渲染的基础;JS 和 Font 是中等优先级的资源,因为它们是页面功能和美观的重要组成部分;Image 和 Video 是最低优先级的资源,因为它们是页面内容和媒体的辅助元素。

使用 preload 标签可以提高资源的优先级,让浏览器尽早地请求并缓存资源,以便后续使用。这对于那些延迟加载或动态加载的资源,如 JS、Font、Image 和 Video 等,非常有用,因为它们可能会影响页面的渲染或交互。

使用 prefetch 标签可以降低资源的优先级,让浏览器在空闲时间去请求并缓存资源,以便后续使用。这对于那些将来可能会被用到的资源,如 JS、Font、Image 和 Video 等,非常有用,因为它们可以提高后续页面或操作的响应速度和用户体验。

与其它加载方式对比

除了 preloadprefetch 标签之外,还有一些其它的加载方式可以用来优化应用的性能和用户体验。这里我们将简要地介绍一些常见的加载方式,并与 preloadprefetch 标签进行对比。

async/defer

asyncdefer 属性可以用来控制脚本的加载和执行时机。它们都可以让浏览器在解析 HTML 的同时,并行地加载脚本,从而避免阻塞渲染。不同的是,async 属性会让浏览器在脚本加载完成后立即执行脚本,而不管 HTML 是否解析完毕;defer 属性会让浏览器在 HTML 解析完毕后按照脚本的顺序依次执行脚本。例如:

<script src="script1.js" async></script>
<script src="script2.js" defer></script>

这样,浏览器会同时加载 script1.jsscript2.js,但是 script1.js 会在加载完成后立即执行,而 script2.js 会在 HTML 解析完毕后执行。这意味着 script1.js 可能会在 script2.js 之前或之后执行,取决于它们的加载速度。

preloadprefetch 标签的区别是:

  • asyncdefer 属性只适用于脚本,而 preloadprefetch 标签可以适用于任何类型的资源。
  • asyncdefer 属性会让浏览器自动执行脚本,而 preloadprefetch 标签只会让浏览器请求并缓存资源,不会自动执行或应用资源。
  • asyncdefer 属性会影响脚本的执行顺序和时机,而 preloadprefetch 标签不会影响资源的使用顺序和时机。

HTTP/2 Server Push

HTTP/2 Server Push 是一种服务器主动推送资源给客户端的技术,它可以让服务器在响应一个请求时,同时推送一些相关的资源给客户端,从而避免客户端再次发起请求。这样可以减少网络延迟和往返次数,提高资源的加载速度和用户体验。

例如,如果客户端请求了一个 HTML 文件,服务器可以在响应这个文件的同时,推送一些相关的 CSS、JS、Image 等资源给客户端。这样,客户端就不需要再等待 HTML 文件解析完毕后再去请求这些资源,而是可以直接从缓存中读取。例如:

HTTP/2 200 OK
Content-Type: text/html
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

<html>
<head>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <script src="script.js"></script>
</body>
</html>

HTTP/2 200 OK
Content-Type: text/css
...

HTTP/2 200 OK
Content-Type: text/javascript
...

这样,服务器在响应 HTML 文件的同时,也推送了 CSS 和 JS 文件给客户端。客户端在解析 HTML 文件时,就可以直接使用这些文件,而不需要再发起请求。

preloadprefetch 标签的区别是:

  • HTTP/2 Server Push 是由服务器控制的,而 preloadprefetch 标签是由客户端控制的。
  • HTTP/2 Server Push 需要服务器和客户端都支持 HTTP/2 协议,而 preloadprefetch 标签只需要客户端支持 HTML5 标准。
  • HTTP/2 Server Push 可以更早地推送资源给客户端,而 preloadprefetch 标签需要等待 HTML 文件被请求和解析后才能生效。
  • HTTP/2 Server Push 可能会推送一些客户端已经缓存或不需要的资源,而 preloadprefetch 标签可以更精确地指定需要的资源。

浏览器预解析

浏览器预解析是一种浏览器自动预取资源的技术,它可以让浏览器在解析 HTML 时,根据一些线索(如链接、导航、历史等),提前请求一些可能会被用到的资源,并将它们存放在缓存中,以便后续使用。这样可以提高后续页面或操作的响应速度和用户体验。

例如,如果浏览器在解析 HTML 时,发现了一个 <a href="next.html"> 标签,它可能会认为用户有很大的可能会点击这个链接,从而提前请求 next.html 文件,并将其缓存起来。当用户点击这个链接时,就可以直接从缓存中读取,而不需要再等待网络请求。

preloadprefetch 标签的区别是:

  • 浏览器预解析是由浏览器自动进行的,而 preloadprefetch 标签是由开发者手动指定的。
  • 浏览器预解析是基于浏览器的算法和策略的,而 preloadprefetch 标签是基于开发者的逻辑和需求的。
  • 浏览器预解析可能会预取一些不必要或不相关的资源,而 preloadprefetch 标签可以更精确地指定需要的资源。

使用案例

为了更好地理解和利用 preloadprefetch 标签,我们可以参考以下一些使用案例:

  • 预加载字体文件。如果我们使用了自定义的字体文件,那么我们可以使用 preload 标签来提前加载它们,从而避免字体闪烁或延迟显示的问题。例如:
<link rel="preload" as="font" type="font/woff2" href="font.woff2" crossorigin />
  • 预加载关键图片。如果我们有一些关键的图片,例如 logo、banner、图标等,那么我们可以使用 preload 标签来提前加载它们,从而避免图片空白或延迟显示的问题。例如:
<link rel="preload" as="image" href="logo.png" />
  • 预加载关键脚本。如果我们有一些关键的脚本,例如交互、动画、统计等,那么我们可以使用 preload 标签来提前加载它们,从而避免脚本阻塞或延迟执行的问题。例如:
<link rel="preload" as="script" href="script.js" />
  • 预获取后续页面。如果我们有一个多步骤的流程,例如注册、购物、结算等,那么我们可以使用 prefetch 标签来提前获取后续页面的资源,从而提高用户的感知速度和满意度。例如:
<link rel="prefetch" href="register.html" />
<link rel="prefetch" href="cart.html" />
<link rel="prefetch" href="checkout.html" />
  • 预获取相关内容。如果我们有一些相关的内容,例如推荐、评论、广告等,那么我们可以使用 prefetch 标签来提前获取这些内容的资源,从而提高用户的兴趣和参与度。例如:
<link rel="prefetch" href="recommend.html" />
<link rel="prefetch" href="comment.html" />
<link rel="prefetch" href="ad.html" />

总结

preloadprefetch 标签是 HTML5 提供的两个非常有用的特性,它们可以让我们更好地控制资源的加载时机和优先级,从而优化应用的性能和用户体验。本文介绍了这两个标签的基本用法、更多细节点、不同资源浏览器优先级、与其它加载方式的对比,以及一些使用案例。希望本文能够对你有所帮助,也欢迎你在评论区留下你的意见和建议。😊

如果您觉得本文对您有用,欢迎捐赠或留言~
微信支付
支付宝

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注