预加载资源 preload
优化下一个页面显示的其中一个步骤,是可以在当前页面提前加载资源。包括css
/'js'/'image'等。这时我们就可以使用到preload
了。
preload 提供了一种声明式的命令,让浏览器提前加载指定资源(加载后并不执行),在需要执行的时候再执行。提供的好处主要是
- 将加载和执行分离开,可不阻塞渲染和 document 的 onload 事件
- 提前加载指定资源,不再出现依赖的 font 字体隔了一段时间才刷出
一、如何使用 preload?
<link rel="preload" href="/path/to/style.css" as="style" />
<script>const link = document.createElement("link");
link.rel = "preload";
link.as = "style";
link.href = "/path/to/style.css";document.head.appendChild(link);script>
说明:preload 不仅仅支持 css 的预加载,上述只是使用 css,作为样例。那么具体支持哪些呢?
二、注意事项
- 避免滥用 preload: 若不确定资源是必定会加载的,则不要错误使用 preload,以免本末倒置,给页面带来更沉重的负担。
- 避免混用 preload 和 prefetch preload 和 prefetch 混用的话,并不会复用资源,而是会重复加载。
- 避免错用 preload 加载跨域资源。 crossorigin 属性在加载字体的时候是必须的,即便字体没有跨域是在自己公司的服务器上,因为用户代理必须采用匿名模式来获取字体资源。为什么?请见这里和阿里:preload 预加载资源
三、不同资源加载的优先级规则
我们先来看一张图:
一共分成五个级别:
- Highest 最高
- Hight 高
- Medium 中等
- Low 低
- Lowest 最低
html 主要资源,其优先级是最高的
css 样式资源,其优先级也是最高的
script 脚本资源,优先级不一
前三个 js 文件是写死在 html 中的静态资源依赖,后三个 js 文件是根据首屏按需异步加载的组件资源依赖,这正验证了这个规则。
font 字体资源,优先级不一
css 样式文件中有一个 @font-face 依赖一个 font 文件,样式文件中依赖的字体文件加载的优先级是 Highest;在使用 preload 预加载这个 font 文件时,若不指定 crossorigin 属性(即使同源),则会采用匿名模式的 CORS 去加载,优先级是 High
看下图对比:
第一条 High 优先级,也就是 preload 的请求:
第二条 Highest 也就是样式引入的请求:
可以看到,在 preload
的请求中,缺少了一个 origin
的请求头字段,表示这个请求是匿名的请求
。让这两个请求能共用缓存的话,目前的解法是给 preload 加上 crossorigin 属性,这样请求头会带上 origin, 且与样式引入的请求同源,从而做到命中缓存:
<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" />
四、预加载使用场景
字体提前加载
eb 字体是较晚才能被发现的关键资源中常见的一种。但是在用户体验对前端来说至关重要的现阶段前端开发来说,web 字体对页面的渲染也是至关重要。字体的引用被深埋在 css 中,即便预加载器有提前解析 css,也无法确定包含字体信息的选择器是否会真正作用在 dom 节点上。所以为了减少 FOUT(无样式字体闪烁,flash of unstyled text )需要预加载字体文件,有了 preload,一行代码搞定。
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />
注意:
crossorigin
属性在加载字体的时候是必须的,即便字体没有跨域是在自己公司的服务器上,因为用户代理必须采用匿名模式来获取字体资源(为什么会这样呢?)。
动态加载,但不执行
通常我们可能想在当前页去加载下一页的资源,但是在媒婆 preload 的情况下,我们常常使用动态创建 script 标签的形式,但是动态创建 script 标签的话,js 代码会立即执行。在有了preload
之后,就可以做到动态加载,延迟执行。
let link = document.createElement("link");
link.href = "myscript.js";
link.rel = "preload";
link.as = "script";
document.head.appendChild(link);
上面这段代码可以让你预先加载脚本,下面这段代码可以让脚本执行
let script = document.createElement("script");
script.src = "myscript.js";
document.body.appendChild(script);
基于标记语言的异步加载
<link rel="preload" as="style" href="asyncstyle.css" onload="this.rel='stylesheet'" />
preload 的 onload 事件可以在资源加载完成后修改 rel 属性,从而实现非常酷的异步资源加载。
脚本也可以采用这种方法实现异步加载
难道我们不是已经有了,虽好,但却会阻塞 window 的 onload 事件。某些情况下,你可能希望这样,但总有一些情况你不希望阻塞 window 的 onload 。
举个例子,你想尽可能快的加载一段统计页面访问量的代码,但又不愿意这段代码的加载给页面渲染造成延迟从而影响用户体验,关键是,你不想延迟 window 的 onload 事件。
有了 preload, 分分钟搞定。
<linkrel="preload"as="script"href="async_script.js"onload="let script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);"
/>
五、浏览器兼容性检测
其浏览器支持如下:
const isPreloadSupported = () => {
const link = document.createElement("link");
const relList = link.relList;
if (!relList || !relList.supports) {
return false;
}
return relList.supports("preload");
};