定义
preload 是一个预加载关键字。它显式地向浏览器声明一个需要提前加载的资源。尽早加载资源,但是要等到合适的时机再执行。
- 资源加载:浏览器从服务器获取资源文件的过程。
- 资源执行:浏览器对已加载的资源进行解析、编译或渲染的过程。
使用方式
- 在
<head>中写入<link rel="preload" href="some-resource-url" as="xx">(包括用 JS 创建<link>元素并插入到<head>) - 在 HTTP 头部加上
Link: <some-resource-url>; rel=preload; as=xx
当浏览器“看”到这样的声明后,就会以一定的优先级在后台加载资源。加载完的资源放在 HTTP 缓存中。而等到要真正执行时,再按照正常方式用标签或者代码加载,即可从 HTTP 缓存取出资源。
使用 Preload 加载资源的方式有以下几个特点:
- 提前加载资源
- 资源的加载和执行分离
- 不延迟网页的 load 事件(除非 Preload 资源刚好是阻塞 window 加载的资源)
其他加载方式
预测解析
浏览器预测解析(Preload Scanner) 是浏览器引擎中的一种优化机制,用于在解析主 HTML 文档的同时,提前发现并加载关键资源(如脚本、样式、图片等),从而显著减少页面加载时间。它的核心目的是解决传统解析器的“阻塞”问题,实现资源加载与 HTML 解析的并行化。
浏览器很聪明,它可以在解析 HTML 时收集外链资源。并将它们添加到一个列表,在后台并行下载。预测解析也实现了提前加载以及加载和执行分离。如图所示:
- 仅限于解析 HTML 时收集的外链资源。如果是程序里异步加载的资源无法提前收集到。
- 浏览器不暴露类似于 Preload 中的 onload 事件,也就无法更细粒度控制资源的执行。
async
async 脚本是一加载完就立即执行,因此会阻塞 window 的 onload 事件。而且目前 async 仅限于脚本资源。
Preload 可以实现 async 一样的异步加载功能。且不局限于脚本。比如以下代码实现了加载完 CSS 文件立即作用到网页的功能:
<link rel="preload" href="style.css" as="style" onload="this.rel='stylesheet'">
注:如果页面存在同步阻塞脚本,等脚本执行完后,样式才会作用到网页。这样是因为 Preload 的资源不会阻塞 window 的 onload 事件。
defer
defer 实现了资源的加载和执行分离,并且它能保证 defer 的资源按照在 HTML 里的出现顺序执行。跟 async 一样,目前也只能作用于脚本资源。
Preload 则适用多种资源类型。Preload 的资源也能像 defer 的资源一样延迟执行并保证执行顺序。
Server Push
HTTP/2 的 Server Push 也实现了资源的提前加载以及加载执行分离。不过 Server Push 节省了一个网络来回。我们可以结合 Server Push 优化 Preload,比如服务器识别到文档里的 Preload 的资源就主动推送 Preload 的资源。
如果不希望服务器推送,则可以增加 nopush 属性:
Link: </app/style.css>; rel=preload; as=style; nopush
另外 Server Push 只能推送同域资源。而 Preload 则可以支持跨域资源。 任何你想要先加载后执行,或者想要提高页面渲染性能的场景都可以使用 Preload。
典型用例
- 在单页应用中,提前加载路由文件,提高切换路由时的渲染速度。现在大型的单页应用通常会异步加载路由文件。当用户切换路由时再异步加载相应的模块存在性能问题。可以用 Preload 提前加载,提升性能。
- 提前加载字体文件。由于字体文件必须等到 CSSOM 构建完成并且作用到页面元素了才会开始加载,会导致页面字体样式闪动(FOUT,Flash of Unstyled Text)。所以要用 Preload 显式告诉浏览器提前加载。假如字体文件在 CSS 生效之前下载完成,则可以完全消灭 FOUT。