你不知道的 script 标签的 defer 与 async 属性

我持续组织了近一年的源码共读活动,感兴趣的可以 点此扫码加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信进群。


fa1873dbb5badcd595dcf274f00e9124.png

前言

在面试的时候,经常会遇到一道经典的面试题:

如何优化网页加载速度?

常规的回答中总会有一条:

把 css 文件放在页面顶部,把 js 文件放在页面底部。

那么,为什么要把 js 文件放在页面的最底部呢?

我们先来看下这段代码:

<!DOCTYPE html>
<html lang="zh"><head><title>Hi</title><script>console.log("Howdy ~");</script><script src="https://unpkg.com/vue@3.2.41/dist/vue.global.js"></script><script src="https://unpkg.com/vue-router@4.1.5/dist/vue-router.global.js"></script></head><body>Hello 👋🏻 ~</body>
</html>

他的执行顺序是:

  • 在控制台打印:Howdy ~

  • 请求并执行 vue.global.js

  • 请求并执行 vue-router.global.js

  • 在页面中展示:Hello 👋🏻 ~

  • 触发 DOMContentLoaded[1] 事件

cadebcbd18226ef32c82c2a5c03ad1ae.png
script 加载逻辑

浏览器的解析规则是:如果遇到 script 标签,则暂停构建 DOM,转而开始执行 script 标签,如果是外部 script[2],那么浏览器还需要一直等待其「下载」并「执行」后,再继续解析后面的 HTML。

如果请求并执行「vue.global.js」需要 3 秒,「vue-router.global.js」需要 2 秒,那么页面中的 Hello 👋🏻 ~,则至少需要 5 秒以上才会展示出来。

可以看到,script 标签会阻塞浏览器解析 HTML,如果把 script 都放在 head 中,在网络不佳的情况下,就会导致页面长期处于白屏状态。

在很久以前,一般都是将这些外联脚本,放在 body 标签的最后面,确保先解析展示 body  中的内容,然后再一个个请求执行这些外联脚本。

那有没有其他更优雅的解决方案呢?

答案是肯定的,现在 script  标签新增了 2 个属性:deferasync,就是为了解决此类问题,提升页面性能的。

<script defer>

先看一下 MDN 上的解释:

这个布尔属性被设定用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。

有 defer 属性的脚本会阻止 DOMContentLoaded 事件,直到脚本被加载并且解析完成。

文档是直接总结了他的特性,我们先看看下面的代码,展开说说细节,加深一下理解。

<!DOCTYPE html>
<html lang="zh"><head><title>Hi</title><script>console.log("Howdy ~");</script><script defer src="https://unpkg.com/vue@3.2.41/dist/vue.global.js"></script><script defer src="https://unpkg.com/vue-router@4.1.5/dist/vue-router.global.js"></script></head><body>Hello 👋🏻 ~</body>
</html>

他的执行顺序是:

  • 在控制台打印:Howdy ~

  • 在页面中展示:Hello 👋🏻 ~

  • 请求并执行 vue.global.js

  • 请求并执行 vue-router.global.js

  • 触发 DOMContentLoaded[3] 事件

a27701709cdec0e92f097c0d21305d1b.png
script defer 加载逻辑

如果在 script 标签上设置了 defer 属性,那么在浏览器解析到这里时,会默默的在后台开始下载此脚本,并继续解析后面的 HTML,并不会阻塞解析操作。

等到 HTML 解析完成之后,浏览器会立即执行后台下载的脚本,脚本执行完成之后,才会触发 DOMContentLoaded 事件。

看起来还是蛮好理解的吧?咱们再来讨论 2 个小细节:

Q1: 如果 HTML 解析完成之后,设置了 defer 属性的脚本还没下载完成,会怎样?

A1: 浏览器会等脚本下载完成之后,再执行此脚本,执行完成之后,再触发 DOMContentLoaded 事件。

Q2: 如果有多个设置了 defer 属性的脚本,那浏览器会如何处理?

A2: 浏览器会并行的在后台下载这些脚本,等 HTML 解析完成,并且所有脚本下载完成之后,再按照他们在 HTML 中出现的相对顺序执行,等所有脚本执行完成之后,再触发 DOMContentLoaded 事件。

最佳实践:

建议所有的外联脚本都默认设置此属性,因为他不会阻塞 HTML 解析,可以并行下载 JavaScript 资源,还可以按照他们在 HTML 中的相对顺序执行,确保有依赖关系的脚本运行时,不会缺少依赖。

在 SPA 的应用中,可以考虑把所有的 script 标签加上 defer 属性,并且放到 body 的最后面。在现代浏览器中,可以并行下载提升速度,也可以确保在老浏览器中,不阻塞浏览器解析 HTML,起到降级的作用。

注意:

  • defer 属性仅适用于外部脚本,如果 script 脚本没有 src,则会忽略 defer 特性。

  • defer 属性对模块脚本(script type='module'[4])无效,因为模块脚本就是以 defer 的形式加载的。

<script async>

按照惯例,先看一下 MDN 上的解释:

对于普通脚本,如果存在 async 属性,那么普通脚本会被并行请求,并尽快解析和执行。

对于模块脚本,如果存在 async 属性,那么脚本及其所有依赖都会在延缓队列中执行,因此它们会被并行请求,并尽快解析和执行。

该属性能够消除解析阻塞的 Javascript。

解析阻塞的 Javascript 会导致浏览器必须加载并且执行脚本,之后才能继续解析。

感觉这段描述的已经蛮清晰了,不过咱们还是先看看下面的代码,展开说说细节,加深一下理解。

<!DOCTYPE html>
<html lang="zh"><head><title>Hi</title><script>console.log("Howdy ~");</script><script async src="https://google-analytics.com/analytics.js"></script><script async src="https://ads.google.cn/ad.js"></script></head><body>Hello 👋🏻 ~</body>
</html>

他的执行顺序是:

  • 在控制台打印:Howdy ~

  • 并行请求 analytics.jsad.js

  • 在页面中展示:Hello 👋🏻 ~

  • 根据网络的实际情况,以下几项会无序执行

    • 执行 analytics.js(下载完后,立即执行)

    • 执行 ad.js(下载完后,立即执行)

    • 触发 DOMContentLoaded 事件(可能在在上面 2 个脚本之前,之间,之后触发)

a4e8456dfeac467f77ab975718c7d7b3.png
script async 加载逻辑

浏览器在解析到带有 async 属性的 script 标签时,也不会阻塞页面,同样是在后台默默下载此脚本。当他下载完后,浏览器会暂停解析 HTML,立马执行此脚本。

看起来还是蛮好理解的吧?咱们再来讨论 2 个小细节:

Q1:如果设置了 async 属性的 script 下载完之后,浏览器还没解析完 HTML,会怎样?

A1:浏览器会暂停解析 HTML,立马执行此脚本,等执行完之后,再继续解析 HTML。

Q2:如果有多个 async 属性的 script 标签,那等他们下载完成之后,会按照代码顺序执行吗?

A2:不会。执行顺序是:谁先下载完成,谁先执行。async 的特点是「完全独立」,不依赖其他内容。

最佳实践:

当我们的项目,需要集成其他独立的第三方库时,可以使用此属性,他们不依赖我们,我们也不依赖于他们。通过设置此属性,让浏览器异步下载并执行他,是个不错的优化方案。

注意:

  • async 特性仅适用于外部脚本,如果 script 脚本没有 src,则会忽略 async 特性。

总结

defer

  • 不阻塞浏览器解析 HTML,等解析完 HTML 之后,才会执行 script

  • 会并行下载 JavaScript 资源。

  • 会按照 HTML 中的相对顺序执行脚本。

  • 会在脚本下载并执行完成之后,才会触发 DOMContentLoaded 事件。

  • 在脚本执行过程中,一定可以获取到 HTML 中已有的元素。

  • defer 属性对模块脚本无效。

  • 适用于:所有外部脚本(通过 src 引用的 script)。

async

  • 不阻塞浏览器解析 HTML,但是 script 下载完成后,会立即中断浏览器解析 HTML,并执行此 script

  • 会并行下载 JavaScript 资源。

  • 互相独立,谁先下载完,谁先执行,没有固定的先后顺序,不可控。

  • 由于没有确定的执行时机,所以在脚本里面可能会获取不到 HTML 中已有的元素。

  • DOMContentLoaded 事件和 script 脚本无相关性,无法确定他们的先后顺序。

  • 适用于:独立的第三方脚本。

另外:asyncdefer 之间最大的区别在于它们的执行时机。

One More Thing

你有没有想过,如果一个 script 标签同时设置 deferasync,浏览器会如何处理?

先说结论:从表现形式上来说,async 的优先级比 defer 高,也就是如果同时存在这 2 个属性,那么浏览器将会以 async 的特性去加载此脚本。

这主要分 2 种情况:

如果是「普通脚本」,浏览器会优先判断async属性是否存在,如果存在,则以async特性去加载此脚本,如果不存在,再去判断是否存在defer属性。

如果是「模块脚本[5]」,浏览器会判断async属性是否存在:

  • 如果存在,浏览器会并行下载此模块和他的所有依赖模块,等全部下载完成之后,会立刻执行此脚本。

  • 如果不存在,浏览器也会并行下载此模块和他的所有依赖模块,然后等浏览器解析完 HTML 之后,再执行此脚本。

  • 另外需要注意的是:在模块脚本上设置 defer 属性是无效的。

一图胜千言

最后,用一张图概括一下这两个属性的加载模式吧:

9766f81576b2fba19282cb015abe06ce.png
defer 和 async 的加载模式

思考题 🤔

  • 为什么浏览器在解析到普通的 script 标签时,必须先执行他?

  • 普通的 script 标签会阻塞浏览器解析 HTML,这会导致什么问题?

本文首发于:https://github.com/mrlmx/blogs/issues/4 ,如果喜欢,记得去点个赞哦~ 👍 ❤️

参考

  • https://javascript.info/script-async-defer[6]

  • https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html[7]

  • https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script[8]

  • https://html.spec.whatwg.org/multipage/scripting.html[9]

相关链接

[1]

DOMContentLoaded: https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event

[2]

外部 script: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-src

[3]

DOMContentLoaded: https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event

[4]

script type='module': https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type

[5]

模块脚本: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type

[6]

https://javascript.info/script-async-defer: https://javascript.info/script-async-defer

[7]

https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html: https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

[8]

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script

[9]

https://html.spec.whatwg.org/multipage/scripting.html: https://html.spec.whatwg.org/multipage/scripting.html


8960acaa37cc839ecd12222ae60f4980.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经坚持写了8年,点击查看年度总结。
同时,最近组织了源码共读活动,帮助5000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

596f71fba241b4a4f710904208666a46.jpeg

扫码加我微信 lxchuan12、拉你进源码共读

今日话题

目前建有江西|湖南|湖北 籍 前端群,想进群的可以加我微信 lxchuan12 进群。分享、收藏、点赞、在看我的文章就是对我最大的支持~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274505.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我是怎么调试 Element UI 源码的

我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外&#xff1a;目前建…

模板缓冲_模板缓冲以及如何使用它可视化体积相交

模板缓冲介绍 (Introduction) The trendy thing in real-time rendering these days is ray-tracing. However, traditional rasterization hasn’t disappeared, and it won’t in the near future. I recommend this blog post on the subject: A hybrid rendering pipeline …

重磅!哈啰 Quark Design 正式开源,下一代跨技术栈前端组件库

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

b端 ux 设计思维_借助系统思维从视觉设计过渡到UX

b端 ux 设计思维“How can I switch to UX?” This is a common question from visual designers because there’s a lot of overlap on the surface. But it can also be a difficult transition since UX encompasses much more below the surface.“如何切换到UX&#xff…

三面面试官:运行 npm run xxx 的时候发生了什么?

大家好&#xff0c;我是若川。近期发现好些小伙伴工作有2-3年了&#xff0c;基本不会写脚手架&#xff0c;或者说没学过脚手架。对脚手架大致是如何执行的基本不太知道。其实这类学习资料真的挺多的。而且我们基本天天 npm run dev&#xff0c;应该学习内部实现。不知道的小伙伴…

figma下载_Figma的自动版式实用

figma下载Figma’s Auto Layout has been around for a while, but not everyone’s aware of the benefits it brings. It doesn’t replace constraints, they’re still very much needed. The trick is to use the right feature where necessary. I want to show you how …

Qt通过ODBC读取excel文件

之前替学校考试科用C Builder做过一个小的数据库工具&#xff0c;处理excel表格用的&#xff0c;现在想转换到Qt平台下来&#xff0c;在网上搜了搜有一些利用OBDC读取xls文件的教程&#xff1a; http://hi.baidu.com/kxw102/item/770c496d5736470ca0cf0f1d http://blog.sina.co…

真 · 三面面试官:运行 npm run xxx 的时候发生了什么?

昨晚没权限我只放了链接&#xff0c;今天联系开了白名单。昨天推文主要是为了投票&#xff0c;表明 Node.js 的重要性&#xff0c;有人评论是水文。今天重新转载下。欢迎继续点此去投票。投票显示有高达近80% 表示不太会开发脚手架&#xff0c;看来大多数人确实没有应用场景。可…

ovo svm_反思我在OVO担任远程产品设计实习生的时间

ovo svmIn a quiet bedroom accompanied only by the low humming of my laptop fan, I sat before a Google Hangouts meeting, and got to know my colleagues for the first time, unaware of the joy of a ride that was waiting for me at OVO Design.在一个安静的卧室里&…

最受读者喜爱的前端书 Top 15【留言送书】

最受读者喜爱的前端书Top 15JavaScript高级程序设计&#xff08;第4版&#xff09;| 中文版累计销量32万册&#xff0c;JavaScript“红宝书”全新升级 | 涵盖ECMAScript 2019&#xff0c;全面深入&#xff0c;入门和进阶俱佳 | 结合视频讲解配套编程环境&#xff0c;助你轻松掌…

图文结合简单易学的 npm 包的发布流程

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

拟态防御_拟态从未消失。 这就是为什么。

拟态防御Looking back on design languages, what Apple’s WWDC 2020 Keynote means for the future of design languages, and how we move on from here.回顾设计语言&#xff0c;Apple的WWDC 2020主题演讲对设计语言的未来意味着什么&#xff0c;以及我们如何从这里继续前进…

经常开发后台管理系统,如何提升自己?推荐~【留言送书】

大家好&#xff0c;我是若川。之前送过N次书&#xff0c;可以点此查看回馈粉丝&#xff0c;现在又和博文视点合作再次争取了几本书&#xff0c;具体送书规则看文末。Vue.js是一套用于构建用户界面的渐进式框架。与其他大型框架不同的是&#xff0c;它可以自底向上逐层应用。Vue…

lottie 动画_使用After Effects和Lottie制作网络动画而不会损失质量

lottie 动画A quick getting started guide快速入门指南 I recently took on a project where the team wanted to add some animated icons and a logo. Besides UX & UI design I am also a motion graphic designer so I took on the challenge of doing it with after…

最优秀的技术能力,是技术领导力!

最近和几个刚晋升为技术经理的朋友们约饭&#xff0c;席间互相吐槽职场中的喜怒哀乐&#xff1a; “开始带团队&#xff0c;既担心自己长时间不写代码技术功底退化&#xff0c;又怕手下人干不好&#xff0c;该怎么办&#xff1f;”“我都想回去敲代码了&#xff0c;拼命熬到管理…

模式匹配 怎么匹配减号_如何使您的应用导航与用户的思维模式匹配

模式匹配 怎么匹配减号One of the most interesting things about complex apps is that the navigation itself can be designed to support users’ mental model of the entire experience, thereby increasing engagement and decreasing potential user frustration.复杂应…

ux的重要性_颜色在UX中的重要性

ux的重要性When coming up with a new digital solution (desktop, mobile, app, whatever it may be) or any design concept, choosing the right colour palette is a crucial step that affects its success and outcome. The content, animations, copy and features may …

都2022 年了,你总不能还只会 npm i 吧?

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

matlab学习:图像频域分析之Gabor滤波

很多同学需要源文档&#xff0c;所以添加了下载链接&#xff0c;方便大家共同学习进步~ 本文下载链接&#xff1a;http://files.cnblogs.com/yingying0907/Gabor%E7%AC%94%E8%AE%B0.zip Gabor变换是D.Gabor 1946年提出的。为了由信号的Fourier变换提取局部信息&#xff0c;引入…

云谦:前端框架的趋势与实践

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…