图解script标签中的async与defer属性

1,233 阅读3分钟

前言

script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了。直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲染。

现在大家习惯于在页面中引用各种第三方脚本,如果第三方服务商出现了一些小问题,比如延迟之类的,就会使得页面白屏,针对上述情况,script标签提供了两种方式来解决问题,就是加入属性async以及defer,这两个属性使得script标签加载都不会阻塞DOM的渲染。

而存在以上两个属性,那就说明这两个属性肯定存在差异的。

defer

如果script标签设置了defer属性,则浏览器会异步下载该文件并且不会影响后续DOM的渲染。如果有多个设置了defer属性的script标签存在,则会按照顺序执行所有的scriptdefer脚本会在文档渲染完毕后,DOMContentLoaded事件调用前执行。

针对上述理论,我们编写了一段测试案例,页面包含了两个script标签的加载,给他们都加上了defer标识,其中给script1.js添加了1s的延迟,给script2.js添加了2s的延迟。

image-20210628152514874

下图是页面加载的过程和script脚本的输出顺序。不难看出虽然script1.js的加载时长比script2.js短,但是因为defer的限制,所以script1.js只能等前边的脚本执行完毕后才能执行。

image-20210628153819618 image-20210628155212350

async

async属性会使得script脚本异步的加载并在允许的情况下执行,而async的执行并不会按照script标签在页面中的顺序来执行,而是谁先加载完谁先执行。

修改上述案例代码如下:

image-20210628160022121

下图是页面加载的过程和script脚本的输出顺序。虽然页面加载时长上并没有什么变化,但是可以注意到一个细节的是,DOMContentLoaded事件触发并不受async脚本加载的影响,在脚本加载前就已经触发了DOMContentLoaded

image-20210628160515332 image-20210628160627492

总结

下面来画多个脚本加载时的甘特图,拿四个不同的颜色来标明各自代表的含义:

image-20210628165937483

普通script

文档解析过程中如果遇到script脚本,就会停止页面的解析进行下载,而Chrome会进行一个优化,如果遇到script脚本后会快速查看后面有无需要下载的其他资源,如果有的话,会先下载那些资源,然后再进行下载script所对应的资源,这样能够节省一部分下载的时间。

资源的下载是在解析过程中进行的,虽然说script1.js会比较快加载完毕,但是他前面的script2.js并没有加载并且执行,所以他只能处于一个挂起的状态,等待script2.js执行完毕后再执行。当这两个脚本都执行完毕了再继续解析页面。

image-20210628172149936.png

defer

文档解析时遇到设置了defer属性的脚本,就会在后台进行下载,但是并不会阻止文档的渲染,当页面解析完毕后,会等到所有的defer脚本加载完毕并按照顺序执行,脚本执行完毕后会触发DOMContentLoaded事件。

如果你的脚本代码依赖于页面中的DOM元素(文档是否解析完毕),或者被其他脚本文件依赖,则使用defer属性。

image-20210628172241073.png

async

async脚本会在加载完毕后执行,而async脚本的加载不计入DOMContentLoaded事件统计,下图的两种情况都是有可能发生的。

如果你的脚本并不关心页面中的DOM元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据,则使用async属性。

image-20210628172423241.png image-20210628172641002.png

资料

代码链接:github.com/Wutongjiaoj…

clone后执行npm start即可运行。