超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

script 标签上的 async 与 defer

我们知道,html 在解析过程中,如果遇到外部的脚本,会暂停当前页面的解析过程,而是去下载这个引入的外部脚本,然后执行这个外部脚本,这样无疑会导致当前的页面的渲染被阻塞,这也是为什么建议将脚本文件的加载放在文档的最后。

script 标签新增加了两个布尔属性 deferasync 用于控制脚本的加载和解析,这两个属性的目的都是为了保证引入的外部的脚本不会阻塞当前页面的渲染过程。

<script async src="script.js"></script>
<script defer src="script.js"></script>

其中:

  • defer 指定了脚本在页面在渲染过程中并行加载,但是直到文档解析完成后, DOMContentLoaded 事件触发前执行,效果类似于把 script 标签放在了文档的底部(</body>前)。

  • async 指定了脚本在页面在渲染过程中并行加载,但是和 defer 不同的是,当脚本加载完完成后会立即执行,此时如果页面的还未渲染完成,则渲染过程会被打断。

注意:动态插入的脚本 (如使用 document.createElement) 默认是异步执行的(async=true),如果想要其同步执行,需要加上 async=false

相同点与不同点

这两个属性有以下共同点

  1. 它们在加载的时候不会阻塞当前页面的渲染过程。
  2. 它们只对引入的外部脚本生效,对内联脚本不生效。
  3. 它们是页面渲染中并行加载脚本的,在脚本加载过程中不会打断页面的渲染过程。
  4. document.write 会被忽略,并在控制台上输出类似于这样的错误信息 "A call to "document.write()" from an asynchronously-loaded external script was ignored"

它们的区别在于:

  1. 如果有多个 defer 脚本,defer 脚本的执行是按照 defer 脚本的书写顺序执行的。如果有多个 async 脚本,async 脚本的执行时按照 async 脚本的加载完成的先后顺序执行的,也就是说先加载完的脚本先执行。
  2. defer 脚本不会大段渲染过程,而 async 脚本会打断渲染过程。
  3. 两个属性同时使用的时候会忽略 defer 采用 async 模式。
  4. defer 会在DOMContentLoaded 事件触发前执行,而 async 则会在 load 事件之前执行,但并不能确保与 DOMContentLoaded 的执行先后顺序。

如下图,其实已经明显说明了它们的差异:

The diffent between sync/async/defer

总结

使用这两个属性,可以将脚本的加载放到页面的前面,以便尽可能快的加载完脚本,但是也要根据情况判断使用哪个属性,不能生搬硬套:

比如,如果浏览器不支持这两个属性,放到页面前面加载反而起了负优化的作用。

比如,如果有多个项目依赖的脚本,使用 async 异步加载可能会导致错误,使用 defer 比较好。但是并不是说 async 就没有应用场景了, async 适用于加载不依赖 DOM 的脚本,或者一些独立的小文件,此时我们并不关心文件的执行位置,只需要保证尽快加载完毕,比如加载 GA。

兼容性