这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
问题
今天在review之前的代码,我们之前的上报埋点是通过动态创建script引入的,代码类似如下:
const script = document.createElement('script')
script.src = './test.js' // 在test.js里全局变量`aaa`
document.body.appendChild(script)
console.log(window.aaa) // undefined
然后我们是在test.js里面声明了一个全局变量aaa,我们在appendChild之后打印这个全局变量,才发现是undefined。
what? 这个有点超出我的知识范畴,难道script默认加载不是同步的吗?
js脚本的加载顺序
js脚本的加载分为2种,一种是动态创建script,然后插入body中,一种是通过script标签直接加载。
动态创建script
动态创建script默认加载js脚本和执行是异步的,后面的代码不会等js脚本加载执行后再执行。所以我打印出来的就是undefined。
script标签加载
而script标签加载js脚本和执行默认是同步的,会阻塞后续代码的执行,直到js脚本加载和执行后才会继续执行。
那如果要支持异步怎么办?
别急,上面说的只是默认情况,script标签还支持2个属性,async和defer。
async
async属性是指加载js脚本是异步的,可以和后面的html文档的渲染同时进行,但是js脚本的执行是同步d,也就是说加载脚本的过程是异步的,但是一加载好就执行,执行是同步的,会阻塞html文档的渲染(如果此时html文档的渲染还没完成)
async会在DOMContentLoaded之前或者之后执行,都有可能
defer
defer属性是指加载js脚本是异步的,可以和后面的html文档的渲染同时进行,但是js脚本的执行会等到html文档渲染完成后再执行。这是它和async不同的地方。
defer会在DOMContentLoaded之前执行
下面通过这个例子来看看
<!-- async.js -->
console.log('async')
<!-- defer.js -->
console.log('defer')
<script src="./async.js" async="async"></script>
<script src="./defer.js" defer="defer"></script>
<script>
console.log('index')
</script>
最后打印的是 index async defer