html & script 标签中 defer 和 async 的区别

132 阅读2分钟

HTML

  • script:会阻碍 HTML 的解析,只有下载好并执行完脚本才会继续解析HTML。
  • async script:解析HTML过程中进行脚本的异步下载,下载成功立马执行,有可能会阻断HTML的解析。
  • defer script:完全不会阻碍HTML的解析,解析完成之后再按照顺序执行脚本。

下面清晰的展示了三种 script 的过程:

5df8625b1bf37d58545ba1a0ab9d16d.png

<script>

浏览器解析HTML的时候,如果遇到一个没有任何属性的script标签,就会暂停解析,先发送网络请求获取改js脚本的代码内容,然后让js引擎执行该代码,当代码执行完毕后恢复解析,整个过程如图下所示:

fdf4a7e0370f1e2f47e169895273156.jpg

可以看到,script阻塞了浏览器对于HTML的解析,如果获取js脚本的网络请求迟迟得不到响应,或者js脚本执行时间过长,都会导致白屏,用户看不到页面内容。

<script async>

当浏览器遇到带有 async 属性的 script 时,请求该脚本的网络请求是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器会暂停解析,先让 JS 引擎执行代码,执行完毕后再进行解析,图示如下:

4708dbe842de9560b41baac5cef384f.jpg

当然,如果在 JS 脚本请求回来之前,HTML 已经解析完毕了,那就啥事没有,立即执行 JS 代码,如下图所示:

308aa69d0f9440fd1a2d62d0394cbb4.jpg

所以 async 是不可控的,因为执行时间不确定,你如果在异步 JS 脚本中获取某个 DOM 元素,有可能获取到也有可能获取不到。而且如果存在多个 async 的时候,它们之间的执行顺序也不确定,完全依赖于网络传输结果,谁先到执行谁。

<script defer>

当浏览器遇到带有 defer 属性的 script 时,获取该脚本的网络请求也是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析并执行 JS 代码,而是等待 HTML 解析完毕再执行 JS 代码,图示如下:

f61d5aa4aa968eb9e51b1d923753f9f.jpg

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

总结

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