preload, prefetch
性能优化 preload prefetch 资源加载
prefetch
prefetch(链接预取)是一种浏览器机制,其利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档。网页向浏览器提供一组预取提示,并在浏览器完成当前页面的加载后开始静默地拉取指定的文档并将其存储在缓存中。当用户访问其中一个预取文档时,便可以快速的从浏览器缓存中得到。—MDN
具体来说,浏览器通过<link rel="prefetch" href="/library.js">标签来实现预加载。
其中rel="prefetch"被称为 Resource-Hints(资源提示),也就是辅助浏览器进行资源优化的指令。
假设我们有一个弹框组件,其内部有一张背景图片,在首页点开弹框后,才会触发图片资源的请求,此时该背景图片会有一段时间的空白;此时就可以在首页使用 prefetch 预先加载该资源。
<head>
...
<link rel="prefetch" href="static/img/background.png" />
...
</head>
此时通过 NetWork 可以看到,在首屏的请求列表中已经出现了背景图 background.png 的加载请求,请求本身看起来和普通请求没什么不同;打开弹框后,network 中增加了一次新的 background.png 访问请求,这个请求的 status 虽然也是 200,但有一个特殊的标记—prefetch cache,表明这次请求的资源来自 prefetch 缓存。这个表现验证了上文中 prefetch 的定义,即浏览器在空闲时间预先加载资源,真正使用时直接从浏览器缓存中快速获取。
preload
preload 与 prefetch 同属于浏览器的 Resource-Hints,用于辅助浏览器进行资源优化。为了对两者进行区分,prefetch 通常翻译为预提取,preload 则翻译为预加载。
元素的 rel 属性的属性值 preload 能够让你在你的 HTML 页面中元素内部书写一些声明式的资源获取请求,可以指明哪些资源是在页面加载完成后即刻需要的。对于这种即刻需要的资源,你可能希望在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能。
简单来说,就是通过标签显式声明一个高优先级资源,强制浏览器提前请求资源,同时不阻塞文档正常 onload。
一个场景是:当前的项目使用了自定义字体,页面首次加载时文字会出现短暂的字体样式闪动(FOUT,Flash of Unstyled Text),在网络情况较差时比较明显。究其原因,是字体文件由 css 引入,在 css 解析后才会进行加载,加载完成之前浏览器只能使用降级字体。也就是说,字体文件加载的时机太迟,需要告诉浏览器提前进行加载,这恰恰是 preload 的用武之地。
此时可以在入口 html 假如如下
<head>
...
<link
rel="preload"
as="font"
href="<%= require('/assets/fonts/Font-Demi.otf') %>"
crossorigin
/>
<link
rel="preload"
as="font"
href="<%= require('/assets/fonts/Font-Regular.otf') %>"
crossorigin
/>
...
</head>
此时查看 Network 会发现字体文件的加载时机明显提前了,在浏览器接收到 html 后很快就进行了加载。
使用场景
从前文的介绍可知,preload 的设计初衷是为了尽早加载首屏需要的关键资源,从而提升页面渲染性能。
目前浏览器基本上都具备预测解析能力,可以提前解析入口 html 中外链的资源,因此入口脚本文件、样式文件等不需要特意进行 preload。
但是一些隐藏在 CSS 和 JavaScript 中的资源,如字体文件,本身是首屏关键资源,但当 css 文件解析之后才会被浏览器加载。这种场景适合使用 preload 进行声明,尽早进行资源加载,避免页面渲染延迟。
与 preload 不同,prefetch 声明的是将来可能访问的资源,因此适合对异步加载的模块、可能跳转到的其他路由页面进行资源缓存;对于一些将来大概率会访问的资源,如上文案例中的背景图、常见的加载失败 icon 等,也较为适用。
小结
- 大部分场景下无需特意使用 preload
- 类似字体文件这种隐藏在脚本、样式中的首屏关键资源,建议使用 preload
- 异步加载的模块(典型的如单页系统中的非首页)建议使用 prefetch
- 大概率即将被访问到的资源可以使用 prefetch 提升性能和体验
在 vue-cli 中
-
preload 默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。这些提示会被
@vue/preload-webpack-plugin注入,并且可以通过chainWebpack的config.plugin('preload')进行修改和删除。 -
prefetch 默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件(通过动态 import()按需 code splitting 的产物)自动生成
prefetch提示。这些提示会被@vue/preload-webpack-plugin注入,并且可以通过chainWebpack的config.plugin('prefetch')进行修改和删除。
当我们配置了路由懒加载后,默认情况下 webpack 在构建时会对所有的懒加载资源进行prefetch和preload,所以当你打开首页时,会看到大量的prefetch和preload请求:

所以很多性能优化的方案中会添加如下代码:
chainWebpack: (config) => {
config.plugins.delete("prefetch");
config.plugins.delete("preload");
};