玩转代码|Script 标签中的async与defer属性详细分析

这篇具有很好参考价值的文章主要介绍了玩转代码|Script 标签中的async与defer属性详细分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

玩转代码|Script 标签中的async与defer属性详细分析,笔记,vue.js,前端,javascript,StableDiffusion

前言

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

如何优化网页加载速度?

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

把 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事件

玩转代码|Script 标签中的async与defer属性详细分析,笔记,vue.js,前端,javascript,StableDiffusion

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

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

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

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

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

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

<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事件

玩转代码|Script 标签中的async与defer属性详细分析,笔记,vue.js,前端,javascript,StableDiffusion

如果在 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'></script>​ ​)无效,因为模块脚本就是以 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.js 和 ad.js
  • 在页面中展示:Hello 👋🏻 ~
  • 根据网络的实际情况,以下几项会无序执行
    • 执行 analytics.js (下载完后,立即执行)
    • 执行 ad.js (下载完后,立即执行)
    • 触发​ DOMContentLoaded ​事件(可能在在上面 2 个脚本之前,之间,之后触发)

玩转代码|Script 标签中的async与defer属性详细分析,笔记,vue.js,前端,javascript,StableDiffusion


浏览器在解析到带有 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 脚本无相关性,无法确定他们的先后顺序。
  • 适用于:独立的第三方脚本。

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

One More Thing

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

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

这主要分 2 种情况:

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

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

  • 如果存在,浏览器会并行下载此模块和他的所有依赖模块,等全部下载完成之后,会立刻执行此脚本。
  • 如果不存在,浏览器也会并行下载此模块和他的所有依赖模块,然后等浏览器解析完 HTML 之后,再执行此脚本。
  • 另外需要注意的是:在模块脚本上设置 defer 属性是无效的。

一图胜千言

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

玩转代码|Script 标签中的async与defer属性详细分析,笔记,vue.js,前端,javascript,StableDiffusion文章来源地址https://www.toymoban.com/news/detail-552288.html

到了这里,关于玩转代码|Script 标签中的async与defer属性详细分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • defer 和 async:JavaScript异步编程的利器

    🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_ CSDN 博客专家、23年度博客之星前端领域TOP1 🕠 牛客 高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课 签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你

    2024年03月24日
    浏览(55)
  • 【正则表达式】获取html代码文本内所有<script>标签内容

    一. 背景 之前要对学生提交的html代码进行检查,在获取了学生提交的html代码文本后,需要使用正则去截取内部的script标签内容做进一步的检查。 假设得到html文本如下(不是代码),我们要得到全部的script标签内容并提取出来。 看上去不难,但是实际操作起来有一定的坑,

    2024年01月17日
    浏览(50)
  • 玩转数据-大数据-Flink SQL 中的时间属性

    时间属性是大数据中的一个重要方面,像窗口(在 Table API 和 SQL )这种基于时间的操作,需要有时间信息。我们可以通过时间属性来更加灵活高效地处理数据,下面我们通过处理时间和事件时间来探讨一下Flink SQL 时间属性。 2.1、准备WaterSensor类,方便使用 2.2、DataStream 到

    2024年02月07日
    浏览(45)
  • meta 标签中的 viewport 相关属性

    meta 标签中的 viewport 相关属性: initial-scale 属性用于设置页面初始的缩放比例,缩放比例为理想视口与视觉视口的比值。 -------------------- width:    页面宽度,正整数或 device-width(设备宽度),定义视口的宽度,单位为像素。 height:    页面高度,正整数或 device-height(设备高度

    2024年02月07日
    浏览(57)
  • 详细分析python中的 async 和 await(附Demo)

    对于异步的基本知识推荐阅读我之前的文章: 详细讲解Python中的aioschedule定时任务操作 java关于@Async异步调用详细解析附代码 【操作系统】线程与进程的深入剖析(全) 【操作系统】守护线程和守护进程的区别 在Python中, async 和 await 是用于异步编程的,引入了异步

    2024年04月28日
    浏览(34)
  • Swift 中的 async/await ——代码实例详解

    async-await 是在 WWDC 2021 期间的 Swift 5.5 中的结构化并发变化的一部分。Swift 中的并发性意味着允许多段代码同时运行。这是一个非常简化的描述,但它应该让你知道 Swift 中的并发性对你的应用程序的性能是多么重要。有了新的 async 方法和 await 语句,我们可以定义方法来进行异

    2023年04月12日
    浏览(42)
  • 如何批量修改删除html文件中的标签属性

    最近工作中遇到一个问题,一份html文档因为内容里面的样式标签过多导致文件整体过大。 这些描述标签不是必须的,现在需要优化删除掉这些标签从而减小文件体积。 对于这种批量修改删除的任务,我们首先想到的就是使用编辑器处理。 编辑html文档,我使用的是VS Code,它

    2024年02月01日
    浏览(64)
  • HTML <iframe> 标签的常用属性--详解(附加代码)

    iframe 标签用于在网页中嵌入另一个文档(通常是外部网页)或者内嵌内容。以下是 iframe 标签的示例代码和一些常用属性: 通过使用 iframe 标签,可以将其他网页的内容嵌入到当前网页中,并提供许多可选属性来控制它们的行为和样式。上述示例演示了基本的使用方法以及一

    2024年02月12日
    浏览(43)
  • HTML中a标签的target属性的取值和作用--详解(附加代码)

    a标签的 target 属性用于指定链接文档在何处显示。以下是 target 属性的常见取值和对应的作用: 1. _self : 默认值。链接文档会在当前窗口或者框架中打开。 2. _blank : 链接文档会在新窗口或者新标签页中打开。 3. _parent : 链接文档会在父级框架中打开,如果没有父级框架,则与

    2024年02月12日
    浏览(41)
  • 【HTML5】HTML5 多媒体标签 ① ( audio 音频标签 | 音频标签常见属性值设置 | 音频标签默认代码设置 | 音频标签设置多种类型音频文件 )

    传统 HTML 开发中 , 如果想要向网页中嵌入音频和视频 , 需要 使用 Flash 浏览器插件才能实现 ; 在 HTML5 中 , 使用 多媒体标签 , 即可实现向浏览器中插入音视频 , 多媒体标签如下 : 音频标签 : audio 视频标签 : video HTML 5 的 audio 音频标签 , 支持 ogg / mp3 / wav 三种格式的音频 , 不同的

    2024年02月15日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包