1

彻底搞懂 defer & async

DOMContentLoaded

image_1c443p1c3kkv6k414ri1l5k32m26.png-373.7kB

通过 chrome 浏览器的开发者工具可以直观的看到,图中蓝色的线和蓝色的字使用不同的表现形式表示 DOMContentLoaded 这个事件触发的时间。

我们先来思考一个问题,如何衡量一个网页的加载速度?

在日常生活中,很多时候因为网络原因我们并不需要等待网页上的所有内容都加载完毕,而是只需要加载主要内容即可,比如你打开这篇博客时,可能并不需要等待所有图片都加载完成,而是看到博客的正文就可以正常阅读。也就是说,用户有时候只需要在空白的网页上看见内容就可以,而不需要等待所有内容都加载出来。

那么如何衡量“计算这个网页从空白到出现内容所花费的时间”呢?HTML5 规范已经帮我们完成相应的工作,即当一个 HTML 文档被加载和解析完成后,DOMContentLoaded 事件便会被触发。

浏览器向服务器请求到 HTML 文档后便开始解析(其产物是 DOM),到这里 HTML 文档就可以说是被加载和解析完成,同时如果有 CSS 文件则会根据 CSS 生成 CSSOM,然后再合并 DOM 和 CSSOM 生成渲染树,至此我们已经知道所有节点的样式,下面便根据这些节点以及样式计算它们在浏览器中确切的大小和位置(即布局阶段),最后得到以上这些信息后,就可以把节点绘制到浏览器上。

image_1c4446ec51efa4guc0apa7gh72j.png-19.5kB

下面我们要加入考虑 JavaScript:JavaScript 可以阻塞 DOM 的生成,也就是说当浏览器在解析 HTML 文档时,如果遇到(同步)脚本则停止解析,先去加载脚本并执行,执行结束后继续解析 HTML 文档。

image_1c444h7sacvrctq91od5phrl30.png-1.2kB

defer

当 HTML 文档被解析时如果遇到 defer 脚本,则在后台加载脚本,文档解析过程不中断,等待文档解析结束之后,defer 脚本执行。

image_1c444j2o215um1poo1gl16kq12un3d.png-1.1kB

另外,defer 脚本的执行顺序与定义时的位置有关。

如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行,而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。

HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成,在 DOM & CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。

async

当 HTML 文档被解析时如果遇到 async 脚本,则在后台加载脚本,文档解析过程不中断,脚本加载完成后,文档停止解析并执行脚本,执行结束后文档继续解析。

image_1c444knev1ch711jsa5pejt18an4q.png-1.2kB

当脚本下载完后立即执行,执行顺序不确定。

如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后 DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等。

其他

  • 我们在 jQuery 中经常使用的 $(document).ready(function() { // ...代码... }); 其实监听的就是 DOMContentLoaded 事件
  • 如果 script 无 src 属性,则 defer, async 会被忽略
  • 动态添加的 script 标签隐含 async 属性

10081677wc
272 声望14 粉丝