浏览器加载JavaScript文件

在HTML中,我们可以使用script标签来加载外部脚本或内联脚本,对于外部脚本来说也可以通过在内联脚本中使用append的方式插入到DOM中。在使用webpack的过程中,通过配置可将公共的依赖打包成vendor.js文件,以及项目的主入口如main.js文件,在开启懒加载的情况下,会根据页面功能依赖通过js动态创建标签的方式动态的加载所需的js文件。

script标签

在常规的html代码中,使用script标签加载的js文件的方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
</head>

<body>
<div id="app">{{message}}</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
</script>

</html>

通过上述代码我们使用script加载了外部和内联两种脚本,其中内联脚本的执行依赖于外部脚本。所以我们将内敛脚本script标签放在依赖的外部脚本script标签之后。
那如果将顺序反过来会出现什么情况呢,运行后会发现控制台的报错

1
Uncaught ReferenceError: Vue is not defined

所以这里有个疑问,vue的cdn文件会保证在new Vue之前加载吗,假设vue的cdn文件很慢,浏览器会保证加载顺序一定是按照我们所写的顺序进行加载吗?以script标签的形式加载时浏览器是串行加载的吗?

首先,浏览器会保证加载的顺序是按照我们写的script标签的顺序来依次加载并执行,并且确保是串行的形式加载。当vue的cdn文件很慢的情况下,页面会一直卡在加载vue的cdn文件的位置,等待文件加载完成。这里就会有一个疑问,浏览器到底是怎么工作的呢?

  • 对于script文件有装载(下载)和执行(解析)两个阶段
  • 所有script标签引入的 JavaScript 会按照他们引入的顺序依次被解析,在没有使用 defer 或者 async 的情况下,只有在解析完前面 script元素中的代码之后,才会开始解析后面script元素中的代码。

那么动态创建的script标签是什么样的执行顺序呢?

动态创建script

对于动态创建的script标签,浏览器的加载和执行都是异步的,如果后续的执行依赖于异步加载的js文件,为了保证正常的执行,需要在script标签onload的时候添加回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
</head>
<body>
<div id="app">{{message}}</div>
</body>
<script>
function loadScript(src, cb) {
let el = document.createElement('script');
el.type = "text/javascript";
el.src = src;
document.getElementsByTagName('head')[0].appendChild(el);

cb = cb || function() {};
el.onload = function (e) {
cb()
}
}

loadScript('https://cdn.jsdelivr.net/npm/vue', function() {
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
});
</script>
</html>

对于异步加载js脚本,需要考虑的是什么时候用defer以及什么时候用async?
常规情况下只需要考虑脚本之间是否有依赖关系,如果有依赖关系,则需要保证顺序执行,使用defer,没有的话使用async。