在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。