浏览器渲染线程在解析
HTML文档,遇到script标签时,有一下几种不同的加载方式:
普通script(不添加async、defer异步属性)
<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脚本,且页面解析渲染完, 才会依此触发DOMContentLoaded、loaded事件; - 考虑到不支持
defer和async的老浏览器, 最好的方式是将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>
- 文档解析时,遇到设置了
defer的script标签时,会在后台进行下载(下载并不会阻止文档的解析渲染); - 多个
defer脚本是并行下载的, 但是按照顺序依次执行的; - 当页面解析渲染完后, 会等所有
defer脚本下载完并按照顺序执行,执行完后会触发DOMContentLoaded事件; - 等页面解析渲染完毕后, 触发
DOMContentLoaded事件前,defer脚本才会依次执行,所以如果defer脚本下载、执行慢, 会延迟DOMContentLoaded事件的触发时间;- 如果
defer脚本下载较快, 会等到页面解析渲染完毕后, 按照顺序执行defer脚本,执行完毕后会触发DOMContentLoaded事件;· - 如果
defer脚本下载较慢, 在下载完成前, 页面解析渲染已完毕,这时会等defer脚本下载完, 并按照顺序执行defer脚本之后,才会触发DOMContentLoaded事件;
- 如果
总结
注:
defer、async只对外联script脚本文件有效, 内联script脚本设置无效;- 加上
async或defer的js脚本(如果不考虑兼容性),放在HTML头部可以减少网页的下载加载时间,优化页面加载的性能; - 若
defer和async属性同时存在,则async起作用;
参考原文:
script标签中defer和async
script标签中的defer和async
DOMContentLoaded与load的区别、触发时机