前端优化性能 —— 用 preload 和 prefetch 预加载脚本
一、<link>
标签
<link>
是一个具有很多功能的标签。平时我们常用的是加载样式表和 favicon
:
<link ref="stylesheet" href="index.css">
<link ref="icon" href="favicon.icon">
其实它还有一些有意思的性能和安全特性:
比如 preload
一张图片,让图片在使用时更快地显示:
<link rel="preload" href="logo.png" as="image">
又比如在网络空闲时 prefetch
一个后面可能会异步加载的模块,以便在 import('./a').then
时可以迅速打开:
<link rel="prefetch" href="a.js" as="script">
我们今天就重点讨论 js 加载的场景:
二、比较三种加载脚本
我们比较使用 preload
、prefetch
和不预加载三种方式加载脚本有什么不同。
创建文件
首先我们创建 index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="prefetch" as="script" href="./b.js">
<link rel="preload" as="script" href="./a.js">
</head>
<body>
<button id="button-a">get scripts</button>
<script src="./main.js"></script>
</body>
</html>
主脚本 main.js,点击按钮后会加载三个脚本:
// main.js
console.log('main')
function addScript(path) {
const script = document.createElement('script')
script.src = path
document.head.appendChild(script)
}
window.addEventListener('DOMContentLoaded', (event) => {
console.log('DOMContentLoaded');
});
window.addEventListener('load', (event) => {
console.log('load');
});
document.getElementById('button-a').addEventListener('click', () => {
addScript('./a.js')
addScript('./b.js')
addScript('./c.js')
})
以及 a.js, b.js, c.js 三个异步加载的脚本
// a.js
console.log('a')
// b.js
console.log('a')
// c.js
console.log('a')
为了让网络看上去更加真实,我们给每个文件加上几大段注释,用来增加文件的大小。
网路设置
在控制台的 Network 中,不要打开 Disable cache
,网络选择 Fast 3G
:
表现
第一步:进入页面
首先打开控制台,输入网址进入页面:
可以看到:
preload
的 a.js 会先于 main.js 加载;prefetch
的 b.js 会在 main.js 之后加载,尽管它的位置在<script src="main.js">
之前- 蓝色的线表示
DOMContentLoaded
,红色的线表示Load
,可见prefetch
的加载不会阻塞 DOM 就绪
然后控制台打印出来的是:
main
DOMContentLoaded
load
第二步:点击按钮
接下来我们点击按钮,这个时候网络会增加两条:
可以看到:
- 通过
preload
的 a.js,不会再额外发送请求。 - 通过
prefetch
的 b.js 会再次发一条请求,但这条请求的 Size 是 prefetch cache,说明它是走强制缓存,不需要再去网络上进行下载,所以非常快。 - 而不进行任何预加载的 c.js,在按钮点击之后才开始下载
- 其中有 Priority 这一列,表示优先度。现在出现了
Highest
,High
,Lowest
,Low
四种了
而我们的控制台现在是:
main
DOMContentLoaded
load
a
b
c
变形一:preload 是否会阻塞 DOM 就绪?
我们调整 a.js 和 main.js 的大小,可以看到:
所以是不会阻塞 DOM 就绪的。
变形二:如果 preload 和 prefetch 还没有完成就被请求?
我们在 main.js 刚加载完成时,就迅速点击按钮:
没有预加载完成,就被请求也没有关系,等待它执行完成即可。
变形三:同步请求是否会阻塞 load 事件
把 addScript
移出点击事件处理器,放在脚本根上,或者放在 DOMContentLoaded
事件监听器中:
我们发现 load
事件居然被阻塞了!
然后再把 addScript
套在 setTimeout
里面,发现时间比较小时会阻塞,时间比较大(比如十几毫秒)时不会阻塞。所以这个应该是和系统性能有关系。
三、总结
preload
会按照和script
出现的先后顺序,发出一个High
优先级的请求;而prefetch
会等待高级别优先级的请求发出后,发出一个Lowest
的请求。- 两种预加载方式都是只请求不执行,需要等到
script
标签插入到页面之后再执行它 - 两种预加载方式在“还未加载完成时就被使用到”的情况下,会等待加载完成之后被使用
- 两种预加载方式,都不会阻塞
DOMContentLoaded
(DOM 就绪);而在load
之前添加<script>
标签,会阻塞load
事件发生 prefetch
的资源在使用时还会发一次请求,这次请求走缓存
转载自:https://juejin.cn/post/7140186469687099428