了解异步和延迟的HTML属性
脚本组件是一个重要的HTML标签。它指的是在网页中运行的可执行代码或数据。
下面是一个script 标签的例子。
<script src='javcascriptcode.js'></script>
<!-- the src attribute refers to an executable code or data-->
一个script 标签可以指向一个外部源,甚至包含可执行代码。
在某些情况下,可执行数据可能比HTML文件大。这意味着在下载和处理HTML文件时会有延迟。
浏览器必须先下载和处理script 标签的内容,然后再渲染网页的其他部分。
async 和 属性有助于减少执行脚本标签中代码时的延迟。defer
这些元素对提高网页性能至关重要。它们有利于增强用户体验。
在本教程中,你将了解Async和defer脚本属性。
目标
在这篇文章中,你将学习
- 浏览器如何解析HTML。
- 什么是async和defer。
- 如何使用async或defer。
- async和await之间的异同。
前提条件
要跟上进度,你需要
- 对[HTML]有一些基本的了解。
- 对[Javascript]有一些基本的了解。
- 一个代码编辑器。我们将使用[VS Code]。
关于脚本标签的误解
大多数人认为,script 标签应该在关闭的body 标签之前,如下图所示。
<head></head>
<body>
<H1>My Website</H1>
<p>Welcome to my Portfolio</p>
<script src=’javascriptcode.js’></script><!-- script tag before the closing tag -->
</body> <!-- closing body tag -->
这种假设是由于浏览器解析(加载)HTML的方式造成的。
然而,你可以把script 标签放在HTML文档的任何地方。例如,你可以把它放在header 标签内,或者紧接着开头的body 标签之后,如下图所示。
<head>
<script src=’javascriptcode.js’></script> <!-- script tag within the header tag -->
</head>
<body>
<H1> MY Website</H1>
<p>Welcome to my Portfolio</p>
</body>
浏览器如何解析HTML代码
在我们深入研究async和defer属性之前,你需要了解浏览器如何解析HTML。
解析是指分析程序文件并将其转换为便于运行环境(浏览器)操作的格式。这里,一个程序文件可以是一个HTML文件,也可以是一个JavaScript文件。
对HTML文件的解析通常包括标记化和树状结构。
标记化是将HTML的开头标签、结尾标签、属性名称和值划分为单元,称为tokens 。
树状结构包括使用标记建立文档对象模型(DOM)。
当浏览器解析HTML代码并遇到一个script 标签时,浏览器会暂停执行过程。
然后,浏览器下载该script 的内容,继续解析HTML代码。
但是,如果你有一个HTML文件,在header 标签或body 标签内有许多script 标签。这些标签可能会导致解析HTML文件时出现明显的延迟。
出现这种延迟是因为浏览器在每个脚本标签中暂停了对HTML文件的解析。然后,浏览器会请求script 标签的内容。因此,script 标签下面的HTML内容被屏蔽了。
在下载完script 标签的内容后,HTML文件的解析就可以继续进行。
下面的图片让我们对正常的HTML解析有了更好的了解。

如上图所示。
- 一旦浏览器解析HTML并遇到一个脚本标签,就会暂停HTML解析。
- 然后,浏览器在继续进行HTML解析之前,会获取(下载)并执行该脚本。
一个HTML文件中的许多script 标签可能会导致明显的延迟。对于拥有快速互联网连接的最终用户来说,这个问题可能不会被注意到。
然而,大多数终端用户的网络连接速度较慢,因此可能会注意到这种延迟。我们可以使用defer或async属性来解决这个问题。
<script src='' async></script>
<script src='' defer></script>
了解async和defer属性
在文章的这一部分,你将了解什么是async和defer。
Async和defer属性是script 标签的布尔值,可以消除解析器阻塞的JavaScript。
解析器阻塞JavaScript是一个过程,浏览器在加载和执行
script标签的内容时,会阻止或暂停HTML代码。
延迟
用通俗的话说,defer ,意思是推迟到以后的时间。下面是一个将defer 作为script 标签属性的例子。
<h1>...content before script...</h1>
<script defer src="script.js"></script>
<!-- visible immediately -->
<p>...content after script...</p>
当一个script 标签包含像上面这样的defer ,它就会通知浏览器。
-
当它接触到脚本时,不要阻止HTML文件的解析。浏览器继续构建DOM,所以
script标签之后的内容是可见的。 -
要在后台加载脚本。
-
暂停
DOMContentLoaded事件,直到脚本被完全加载和评估。 -
按顺序执行脚本。它维护
script标签的顺序。例如,long-script.js将在short-script.js之前被首先加载。
<script defer src="long-script.js"></script>
<script defer src="short-script.js"></script>
在上面的例子中,short-script.js 可能因为其大小而先下载。但由于脚本标签中的defer 属性,浏览器将不会执行short-script.js ,直到long-script.js 下载并执行。
当
script标签没有src属性时,defer属性就没有作用。
下面的图片提供了对defer属性的直观理解。

从上面的图片来看。
一旦浏览器解析了HTML并遇到了一个脚本标签。浏览器就会在解析HTML的同时获取(下载)并执行该脚本。
它不会暂停HTML的解析。脚本会在HTML完全解析后执行。
异步
我们从 "异步 "这个词中得到了async,这意味着事件不是在同一时间发生的。
当你在script 标签上添加布尔脚本属性,async 。它通知了浏览器。
-
当它接触到脚本时,不要阻止HTML文件的解析。浏览器会继续构建DOM,所以
script标签之后的内容是可见的。 -
在后台加载脚本,并在脚本加载完毕后执行。带有
async属性的脚本在执行前不需要等待DOMContentLoaded。
异步脚本是独立于其他脚本和DOM的,如下面的例子所示。
<p>...content before scripts...</p>
<script>
document.addEventListener(‘DOMContentLoaded’, () => alert(“DOM is visible!”));
</script>
<script async src=”long-script.js”></script>
<script async src="short-script.js"></script>
<p>...content after scripts...</p>
在上面的例子中。
- 浏览器立即显示该页面。Async不会阻塞
DOMContentLoaded事件。它在HTML内容完全加载之前就显示'DOM是可见的'。
异步脚本通常遵循load-first 的原则。下面的图片提供了一个对async属性的直观理解。

异步脚本与可用的DOM元素一起工作。即使在DOM没有被完全解析的情况下。
例如,如果在一个HTML文件中有25000 按钮,而只有1000 按钮加载,async将在1000 按钮上触发DOMCOntentLoaded 。
Async不会等待其余的24000 按钮再触发DOMContentLoaded 事件。
开始使用
让我们建立一个项目来巩固我们到目前为止所学到的知识。
在这个项目中,你将创建25000 按钮。你将看到defer和async属性是如何执行的。
第1步 - 创建一个项目文件夹
我们需要创建一个新的文件夹,它将包含项目的HTML文件。
在你的代码编辑器中导航到命令行或集成终端。键入下面的命令。
mkdir buttons
使用下面的命令导航到该文件夹。
cd buttons
第2步 - 创建一个HTML文件
在buttons 文件夹中创建一个buttons.html文件。将下面的代码添加到生成的HTML文件中。
<head>
<script src=”defer.js" defer></script>
<script src="async.js" async></script>
</head>
<body>
<div class="container">
<!-- 25000 buttons -->
</div>
</body>
在上面的HTML代码中,我们有两个脚本。第一个脚本带有defers属性,第二个脚本带有async属性。
第3步 - 添加按钮
要创建25000 按钮,把这个链接放在评论下面的**buttons.html** 里面。
然后按tab键。
<head>
<script src="defer.js" defer></script>
<script src="async.js" async></script>
</head>
<body>
<div class="container">
<!-- 25000 buttons -->
button{click}*250000 //press tab after typing this
</div>
</body>
第4步 - 创建JavaScript文件(defer.js)
让我们写一些JavaScript代码来选择HTML代码中的所有按钮。请按照以下说明。
在按钮文件夹中创建一个新的JavaScript文件。把这个JavaScript文件命名为defer.js。
复制下面的代码并粘贴到defer.js 文件中。
// Defer script code
let deferButton = document.querySelectorAll(‘button’);
console.log(`Defer script button count: ${deferButton.length}`);
在上面的代码中,我们选择了HTML文件中的所有buttons 。然后我们将buttons length ,并记录到控制台。
第5步 - 创建JavaScript文件(async.js)
创建一个async.js文件,在async.js文件中添加以下代码。
// Async script code
let asyncButton = document.querySelectorAll(‘button’);
console.log(`Async script button count: ${asyncButton.length}`);
在上面的代码中,我们选择了HTML文件中的所有buttons 。然后我们将按钮的长度记录到控制台。
测试该项目
在浏览器中打开该项目。然后按照下面的说明,打开浏览器的控制台。
-
按f12键,打开开发者工具。
-
在开发者工具的顶部,点击控制台。
在控制台中,你会看到如下图所示的东西。

从上面的图片来看。
-
异步不等待DOM完全加载。异步脚本在其执行时可用的
buttons,即3957上触发DOMContentLoaded。 -
Defer等待DOM元素完全加载。Defer脚本在等待
DOMContentLoaded后选择了所有25000按钮。
async和defer之间的区别
在本节中,我们将看看async 和defer 之间的区别。
| 延迟 | 异步 | |
|---|---|---|
| 加载(执行)顺序 | 脚本标签的出现顺序很重要。 | 脚本标签的出现顺序并不重要。 |
| DOMContentLoaded | 在HTML文件被完全加载和解析后执行,就在DOMContentloaded之前。 | 可以在DOMContentloaded之前或之后执行。 |
何时使用async或defer
什么时候使用async和defer是一个很难做出的决定。然而,我们可以在以下情况下使用defer 。
你可以使用async 。
- 脚本是独立于DOM的,例如广告或google analytics。
- 脚本的相对执行顺序并不重要,例如在广告中。
总结
你已经学会了浏览器如何解析HTML。我们还讨论了async和defer标签属性、它们的区别和使用情况。
Async和defer属性可以消除解析阻塞的JavaScript。