深入探讨JavaScript中script标签的defer与async属性

在网页开发中,JavaScript 脚本的加载和执行时机对页面性能和用户体验有着至关重要的影响。<script>标签的deferasync属性为我们提供了控制脚本加载和执行顺序的有效手段。然而,很多开发者对这两个属性的理解并不深入,导致在实际应用中出现各种问题。本文将深入探讨deferasync的区别,并通过详细的代码示例来帮助大家更好地理解和运用它们。

一、script 标签加载脚本的默认行为

在了解deferasync之前,我们先来回顾一下<script>标签加载脚本的默认行为。当浏览器解析到<script>标签时,会暂停 HTML 的解析,然后下载并执行脚本。只有当脚本执行完毕后,浏览器才会继续解析 HTML。这种行为可能会导致页面渲染的延迟,特别是当脚本文件较大或者网络状况不佳时。

例如,我们有一个简单的 HTML 页面,其中包含一个<script>标签:

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <title>Document</title>

</head>

<body>

   <h1>这是一个标题</h1>

   <script src="script.js"></script>

   <p>这是一段文本</p>

</body>

</html>

在这个例子中,当浏览器解析到<script src="script.js"></script>时,会停止解析 HTML,去下载script.js文件,然后执行该脚本。只有在script.js执行完毕后,才会继续解析<p>标签。如果script.js中包含一些耗时的操作,那么用户可能会看到页面长时间空白,直到脚本执行完成。

二、defer 属性

defer属性的作用是告诉浏览器,在解析完 HTML 文档后,再执行该脚本。也就是说,脚本的加载不会阻塞 HTML 的解析,而是在 HTML 解析完成后,按照<script>标签在 HTML 文档中的顺序依次执行。

1. 语法

<script defer src="script.js"></script>

2. 示例代码

假设我们有两个脚本文件script1.jsscript2.js,以及一个 HTML 页面:

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <title>Document</title>

</head>

<body>

   <h1>这是一个标题</h1>

   <script defer src="script1.js"></script>

   <script defer src="script2.js"></script>

   <p>这是一段文本</p>

</body>

</html>

script1.js的内容如下:

console.log('script1被执行');

script2.js的内容如下:

console.log('script2被执行');

在这个例子中,浏览器会先解析 HTML 文档,当遇到<script defer src="script1.js"></script><script defer src="script2.js"></script>时,会异步加载script1.jsscript2.js,但不会立即执行。当 HTML 解析完成后,会按照<script>标签的顺序,先执行script1.js,再执行script2.js。最终,在浏览器的控制台中会依次输出:

script1被执行

script2被执行

3. 应用场景

defer属性适用于那些需要在页面加载完成后执行,并且需要按照特定顺序执行的脚本。比如,我们可能有一些脚本用于操作 DOM 元素,这些脚本需要在页面的所有元素都被解析之后才能正确执行。又或者我们有一些脚本之间存在依赖关系,需要按照特定顺序加载和执行。

三、async 属性

async属性的作用是让浏览器在下载完脚本后,立即执行该脚本。与defer不同,async不会保证脚本按照<script>标签在 HTML 文档中的顺序执行,而是谁先下载完就先执行谁。

1. 语法

<script async src="script.js"></script>

2. 示例代码

同样假设我们有两个脚本文件script1.jsscript2.js,以及一个 HTML 页面:

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <title>Document</title>

</head>

<body>

   <h1>这是一个标题</h1>

   <script async src="script1.js"></script>

   <script async src="script2.js"></script>

   <p>这是一段文本</p>

</body>

</html>

script1.js的内容如下:

console.log('script1被执行');

script2.js的内容如下:

console.log('script2被执行');

在这个例子中,浏览器会异步加载script1.jsscript2.js。由于async属性的存在,谁先下载完成,就会先执行谁。因此,在浏览器的控制台中可能会输出:

script1被执行

script2被执行

也可能会输出:

script2被执行

script1被执行

具体的输出顺序取决于script1.jsscript2.js的下载速度。

3. 应用场景

async属性适用于那些独立的脚本,它们之间没有依赖关系,并且不需要在页面加载完成后按照特定顺序执行。比如,一些用于统计页面访问量、加载第三方广告等的脚本,这些脚本的执行顺序并不重要,只要尽快执行即可。

四、defer 与 async 的区别总结

执行顺序

defer会按照<script>标签在 HTML 文档中的顺序执行脚本。

async不会保证脚本的执行顺序,谁先下载完就先执行谁。

对 HTML 解析的影响

defer不会阻塞 HTML 的解析,脚本在 HTML 解析完成后执行。

async在下载脚本时不会阻塞 HTML 的解析,但当脚本下载完成后,会立即执行脚本,这可能会阻塞 HTML 的解析。

应用场景

defer适用于需要在页面加载完成后执行,并且需要按照特定顺序执行的脚本,如操作 DOM 元素的脚本、有依赖关系的脚本等。

async适用于独立的、不需要按照特定顺序执行的脚本,如统计脚本、第三方广告脚本等。

五、实际开发中的注意事项

合理选择属性:在实际开发中,要根据脚本的功能和需求,合理选择deferasync属性。如果脚本之间有依赖关系,或者需要在页面加载完成后按照特定顺序执行,应使用defer;如果脚本是独立的,并且希望尽快执行,不关心执行顺序,应使用async

避免过多阻塞:无论是defer还是async,虽然都能在一定程度上减少脚本加载对页面解析的阻塞,但如果有过多的脚本,仍然可能会影响页面性能。因此,要尽量精简脚本,合并不必要的脚本文件。

测试兼容性:不同的浏览器对deferasync属性的支持可能存在差异。在开发过程中,要进行充分的测试,确保在各种主流浏览器中都能正常工作。

总之,深入理解 JavaScript 中<script>标签的deferasync属性的区别,对于优化页面性能、提升用户体验有着重要的意义。希望通过本文的介绍,大家能够在实际开发中更加合理地运用这两个属性。

posted @ 2025-03-08 12:01  heshanwan  阅读(109)  评论(0)    收藏  举报