今天来学习下js脚本的加载顺序

1,260 阅读2分钟

这是我参与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默认加载不是同步的吗?

image.png

js脚本的加载顺序

js脚本的加载分为2种,一种是动态创建script,然后插入body中,一种是通过script标签直接加载

动态创建script

动态创建script默认加载js脚本和执行是异步的,后面的代码不会等js脚本加载执行后再执行。所以我打印出来的就是undefined

script标签加载

而script标签加载js脚本和执行默认是同步的,会阻塞后续代码的执行,直到js脚本加载和执行后才会继续执行。

那如果要支持异步怎么办?

别急,上面说的只是默认情况,script标签还支持2个属性,asyncdefer

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