script标签的defer和async属性详解

1、 defer

该属性用来设置<script>引用的外部脚本在文档完成解析后,触发DOMContentLoaded事件之前执行,属性值为true(defer)/false,默认为false,设置为true时可简写为只写defer

浏览器在解析 HTML 文档时,如果遇到 <script>,便会停下对 HTML 文档的解析,转而去处理脚本。如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,那么先会去加载脚本,然后再执行。等到脚本执行结束后,浏览器才会继续解析 HTML 文档。

但如果页面中存在设置了defer="defer"属性并且是通过src引用的外部脚本的<script>,则该脚本不会阻碍文档的解析,只会在文档解析的同时去加载脚本信息,当文档解析完成后,暂时阻止DOMContentLoaded事件的执行,去执行已经加载好的脚本信息,如果此时脚本还未加载完成,则会等待脚本加载完成后,再执行该脚本。执行完成后,再触发DOMContentLoaded事件。

如果页面上存在多个设置了defer="defer"属性并通过src引用的外部脚本的<script>,浏览器会在文档解析的同时并行的加载这些脚本。当HTML文档解析完成后,会先检测这些脚本是否全部加载完成,若全部加载完成,则会按照它们在HTML文档中的先后顺序从上到下依次执行;若是没有全部加载完成,则会先等待其下载完成后,再按照它们在HTML文档中的先后顺序从上到下依次执行。全部脚本执行结束后,再触发DOMContentLoaded事件。

如果不是通过src引用的外部脚本,而是直接在<script>标签书写内嵌脚本,则设置defer属性无效。除此之外,defer属性对模块脚本也不起作用,因为模块脚本默认defer

如果我们引用的外部脚本1.js需要依赖其他脚本2.js中的信息,也就是需要确保脚本1.js在执行时,脚本2.js已经加载并执行完成,此时我们就可以通过设置脚本1.jsdefer="defer"来实现这种效果。

在Vue项目中,最终打包生成的js文件,在index.html中引用时,都带有defer="defer"属性。

案例代码:
// 正常加载顺序从上到下
<script src="./js/test.js"></script> // console.log('这是普通外链的js文件');
// 设置defer属性
<script defer src="./js/test1.js"></script> // console.log('这是设置了defer属性的外链的js文件');
// 内嵌脚本
<script>
	console.log('这是内嵌的JS脚本');
    document.addEventListener('DOMContentLoaded', function () {
      console.log('这是页面的DOMContentLoaded事件');
    });
</script>
执行结果:

在这里插入图片描述

2、 async

该属性用来设置<script>引用的外部脚本异步加载,不阻塞HTML文档的解析,属性值为true(async)/false,默认为false,设置为true时可简写为只写async

对于普通脚本,如果设置该属性,那么该脚本将会并行加载,不阻碍HTML文档的解析,当加载完成后,如果此时文档还没解析完成,则会终止解析,先执行该脚本,执行结束后再继续解析;如果此时文档已经解析完成了,那就是立即执行该脚本。所以设置该属性的外部脚本的执行时机并不确定,根据网络状况可能会在DOMContentLoaded事件之前,也有可能会在DOMContentLoaded事件之后。所以在脚本里面可能会获取不到在 HTML 中定义的元素,因为此时元素可能还没有被解析。

对于模块脚本来说,如果设置该属性,那么该脚本及其所有依赖将会并行加载,不阻碍HTML文档的解析,加载完成后逻辑与普通脚本相同。

如果页面上存在多个设置了async"属性并通过src引用的外部脚本的<script>,那么浏览器会在解析HTML文档的同时去并行加载这些外部脚本,执行顺序由加载完成顺序决定,谁先加载完成,谁就先执行。

我们在js中通过document.createElement("script");创建的<script>标签,如果我们通过给其src属性设置引用脚本,则这个创建的脚本默认为async异步的。如果我们通过其textContent设置内嵌脚本信息,则这个创建的脚本默认为async同步的。

console.log('这是内嵌的JS脚本');
document.addEventListener('DOMContentLoaded', function () {
    console.log('这是页面的DOMContentLoaded事件');
});
  
// 创建异步脚本
const script = document.createElement("script");
script.src = "./js/test.js";
document.body.appendChild(script);

// 创建同步脚本
const script = document.createElement("script");
script.textContent = "console.log('这是创建出来的同步脚本')";
document.body.appendChild(script);
执行结果:

在这里插入图片描述

生成的页面DOM结构:

在这里插入图片描述

3、defer和async

如果页面上同时存在设置了defer<script>标签和设置了async<script>标签,那么这两个脚本之间的执行顺序是不确定的,两者几乎是同时开始加载的,根据加载完成时机,有下面几种情况:

① 如果async脚本先加载完,defer脚本后加载完,则先执行async脚本。

② 如果defer脚本先加载完,async脚本在文档已经解析完成并且DOMContentLoaded事件之后加载完,则是defer脚本先执行的。

③ 如果很碰巧的在文档解析完成后,DOMContentLoaded事件之前,async的异步脚本加载完成了,并且defer脚本也已经加载完成了,那此时还是async脚本先执行,然后再执行defer脚本,因为async的优先级高于defer

案例代码:
// 正常加载顺序从上到下
// 设置async属性
<script async src="./js/test.js"></script> // console.log('这是async外链的js文件');
// 设置defer属性
<script defer="defer" src="./js/test1.js"></script> // console.log('这是设置了defer属性的外链的js文件');
// 内嵌脚本
<script>
	console.log('这是内嵌的JS脚本');
    document.addEventListener('DOMContentLoaded', function () {
      console.log('这是页面的DOMContentLoaded事件');
    });
</script>
执行结果1:async脚本先执行

在这里插入图片描述

执行结果2:defer脚本先执行

在这里插入图片描述

如果一个<script>标签同时使用了deferasync属性,那此时浏览器将会以async的特性去加载脚本,因为async的优先级高于defer

案例代码:
// 正常加载顺序从上到下
// 同时设置async和defer属性
<script async defer src="./js/test.js"></script> // console.log('这是同时设置了async和defer的脚本');
// 内嵌脚本
<script>
	console.log('这是内嵌的JS脚本');
    document.addEventListener('DOMContentLoaded', function () {
      console.log('这是页面的DOMContentLoaded事件');
    });
</script>
执行结果:

在这里插入图片描述

4、不同scritp的加载和执行时机

在这里插入图片描述

参考文档:

https://blog.csdn.net/mrlmx/article/details/127581208
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script
https://developer.mozilla.org/zh-CN/docs/Games/Techniques/Async_scripts

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小朱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值