script 标签的 defer 和 async 属性

191 阅读3分钟

浏览器渲染线程在解析HTML文档,遇到script标签时,有一下几种不同的加载方式:

普通script(不添加asyncdefer异步属性)

<script src="example1.js"></script>
<script src="example2.js"></script>
<script src="example3.js"></script>
  • 文档解析时,遇到普通的script标签,浏览器会立即下载并执行相应的js脚本,会阻塞页面的解析渲染
  • 如果当前script标签后,有多个script脚本也需要下载时,则会并行下载这些script脚本:
    • chrome做了优化, 遇到script脚本时, 会快速的查看后面有没有需要下载其他资源的, 一起下载, 节省一部分下载的时间;
    • 不论哪个script脚本先下载好, 都会按照HTML中的先后顺序依次执行;
  • 执行完script脚本,且页面解析渲染完, 才会依此触发DOMContentLoadedloaded事件;
  • 考虑到不支持deferasync的老浏览器, 最好的方式是将script放在body底部, 避免阻塞页面的解析渲染;
  • 对于首屏时间而言,script脚本放在HTML文档开头或结尾处,效果是一样的,而script脚本放在结尾的目的并不是为了减少首屏时间,而是由于script脚本经常需要操纵DOM,放在后面才更能保证找到DOM节点,且避免阻塞页面的解析渲染;

script添加async异步属性

<script async src="example1.js"></script>
<script async src="example2.js"></script>
<script async src="example3.js"></script>
  • 文档解析时,遇到设置了async属性的script标签时,会在后台异步下载(下载并不会阻止文档的解析渲染);
  • async脚本的执行可能在DOMContentLoaded事件前, 也有可能在DOMContentLoaded事件后
    • 如果async脚本下载完成后, 文档的解析渲染未完成, 则会立即暂停文档解析,立即执行async脚本,(这种场景, 如果async脚本执行慢, 会延迟DOMContentLoaded事件的触发时间)。
    • 如果async脚本还在下载, 文档的解析渲染已完成, 这时不会等待async脚本下载, 会直接触发DOMContentLoaded事件(这种场景, async脚本的下载和执行不会延迟DOMContentLoaded事件的触发时间);
  • 多个async脚本是并行下载的,但async脚本的执行顺序,不按照HTML中的脚本先后顺序(即那个async脚本先下载完, 就先执行该脚本)

script添加defer异步属性

<script defer src="example1.js"></script>
<script defer src="example2.js"></script>
<script defer src="example3.js"></script>
  • 文档解析时,遇到设置了deferscript标签时,会在后台进行下载(下载并不会阻止文档的解析渲染);
  • 多个defer脚本是并行下载的, 但是按照顺序依次执行的
  • 当页面解析渲染完后, 会等所有defer脚本下载完并按照顺序执行,执行完后会触发DOMContentLoaded事件;
  • 等页面解析渲染完毕后, 触发DOMContentLoaded事件前, defer脚本才会依次执行,所以如果defer脚本下载、执行慢, 会延迟DOMContentLoaded事件的触发时间;
    • 如果defer脚本下载较快, 会等到页面解析渲染完毕后, 按照顺序执行defer脚本,执行完毕后会触发DOMContentLoaded事件;·
    • 如果defer脚本下载较慢, 在下载完成前, 页面解析渲染已完毕,这时会等defer脚本下载完, 并按照顺序执行defer脚本之后,才会触发DOMContentLoaded事件;

总结

script-async-defer.png.png

注:

  • deferasync只对外联script脚本文件有效, 内联script脚本设置无效;
  • 加上asyncdeferjs脚本(如果不考虑兼容性),放在HTML头部可以减少网页的下载加载时间,优化页面加载的性能;
  • deferasync属性同时存在,则async起作用;

参考原文:
script标签中defer和async
script标签中的defer和async
DOMContentLoaded与load的区别、触发时机