script标签的defer和async

355 阅读2分钟

首先提出一个问题:script标签在body或head中会影响阻塞吗?

答案:默认script是同步执行,会发生阻塞。JS脚本存在,会阻塞DOM渲染从而影响页面渲染速度

原因:

  1. 浏览器解析html时,从上往下执行
  2. 解析到DOM中的script时,会暂停DOM构建
  3. 脚本加载并执行完成后,才会继续往下解析
<script src="xxx.js?wait=1000"></script>

async script

<script async="async" src="xxx.js?wait=1000"></script>

async实现异步执行,可以理解是EventLoop中的宏任务。

当浏览器遇到async script时,会执行异步请求脚本,不会阻塞浏览器解析HTML。

  1. 一旦网络请求回来之后,如果HTML还没有解析完,浏览器会暂停解析,先让JS引擎执行代码,执行完毕后再继续解析
  2. 如果JS脚本请求之前,HTML解析完毕,立即执行JS

所以async是不可控的,因为执行时间无法确定

  1. 如果JS脚本中需要获取某个DOM元素,可能获取不到
  2. 如果存在多个async script时,他们之间的执行顺序也不确定,谁先到谁执行

defer script

<script defer="defer" src="xxx.js?wait=1000"></script>

defer同样是延迟加载的意思,不会阻塞浏览器解析HTML

一旦网络请求回来后

  1. 如果此时HTML还没有解析完,浏览器不会暂停解析并执行JS代码,而是等待HTML解析完毕后再执行JS

如果存在多个defer script标签,浏览器会保证按照在HTML中出现的顺序执行,不会破坏JS脚本之间的依赖关系

比较

script标签执行顺序是否阻塞解析HTML
script在HTML中的顺序会阻塞
async script网络请求返回顺序可能阻塞,可能不阻塞
defer scriptHTML中的顺序不阻塞

其他

webpack打包后,html页面中的script使用的是defer