前端优化性能 —— 用 preload 和 prefetch 预加载脚本

lxf2023-05-20 01:24:17

一、<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 加载的场景:

二、比较三种加载脚本

我们比较使用 preloadprefetch 和不预加载三种方式加载脚本有什么不同。

创建文件

首先我们创建 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 和 prefetch 预加载脚本

表现

第一步:进入页面

首先打开控制台,输入网址进入页面:

前端优化性能 —— 用 preload 和 prefetch 预加载脚本

可以看到:

  • preload 的 a.js 会先于 main.js 加载;
  • prefetch 的 b.js 会在 main.js 之后加载,尽管它的位置在 <script src="main.js"> 之前
  • 蓝色的线表示 DOMContentLoaded,红色的线表示 Load,可见 prefetch 的加载不会阻塞 DOM 就绪

然后控制台打印出来的是:

main
DOMContentLoaded
load

第二步:点击按钮

接下来我们点击按钮,这个时候网络会增加两条:

前端优化性能 —— 用 preload 和 prefetch 预加载脚本

可以看到:

  • 通过 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 的大小,可以看到:

前端优化性能 —— 用 preload 和 prefetch 预加载脚本

所以是不会阻塞 DOM 就绪的。

变形二:如果 preload 和 prefetch 还没有完成就被请求?

我们在 main.js 刚加载完成时,就迅速点击按钮:

前端优化性能 —— 用 preload 和 prefetch 预加载脚本

没有预加载完成,就被请求也没有关系,等待它执行完成即可。

变形三:同步请求是否会阻塞 load 事件

addScript 移出点击事件处理器,放在脚本根上,或者放在 DOMContentLoaded 事件监听器中:

前端优化性能 —— 用 preload 和 prefetch 预加载脚本

我们发现 load 事件居然被阻塞了!

然后再把 addScript 套在 setTimeout 里面,发现时间比较小时会阻塞,时间比较大(比如十几毫秒)时不会阻塞。所以这个应该是和系统性能有关系。

三、总结

  • preload 会按照和 script 出现的先后顺序,发出一个 High 优先级的请求;而 prefetch 会等待高级别优先级的请求发出后,发出一个 Lowest 的请求。
  • 两种预加载方式都是只请求不执行,需要等到 script 标签插入到页面之后再执行它
  • 两种预加载方式在“还未加载完成时就被使用到”的情况下,会等待加载完成之后被使用
  • 两种预加载方式,都不会阻塞 DOMContentLoaded(DOM 就绪);而在 load 之前添加 <script> 标签,会阻塞 load 事件发生
  • prefetch 的资源在使用时还会发一次请求,这次请求走缓存
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!