script 标签中 defer 和 async 属性的作用及区别
在现代 Web 开发中,JavaScript 的使用几乎是不可避免的。随着应用逻辑日益复杂,脚本文件体积也在增长。为了优化页面加载性能,浏览器提供了 async 和 defer 两个属性来改变脚本的加载和执行方式。理解它们的差异对于提升用户体验至关重要。
<script> 标签的 defer 属性
工作原理
defer 是一个布尔属性。当存在时,它告诉浏览器不要阻塞 HTML 解析,而是并行下载脚本。关键点是:脚本会在整个 HTML 文档解析完成后,按照它们在文档中出现的顺序执行。
<script defer src="script1.js"></script>
<script defer src="script2.js"></script>
即使 script2.js 比 script1.js 先下载完成,script1.js 也会先执行。这保证了依赖关系的正确性。
代码示例
下面是一个简单的测试结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello, World!</h1>
<script defer src="script1.js"></script>
<script defer src="script2.js"></script>
</body>
</html>
在 script1.js 中:
console.log('Script 1 Loaded!');
在 script2.js 中:
console.log('Script 2 Loaded!');
打开页面后,控制台输出顺序固定为:
Script 1 Loaded!
Script 2 Loaded!
适用场景
使用 defer 的主要优势是允许浏览器继续解析 HTML,使可见内容(文本、图片)更快呈现。同时,它确保脚本在 DOM 构建完成后按序执行,非常适合那些依赖 DOM 元素或彼此有依赖关系的脚本。
<script> 标签的 async 属性
工作原理
async 同样是非阻塞加载,但它不保证执行顺序。脚本一旦下载完成就会立即执行,无需等待其他脚本或 HTML 解析结束。
<script async src="script1.js"></script>
<script async src="script2.js"></script>
哪个脚本先下载完,就先执行哪个。这意味着执行顺序是不确定的。
代码示例
结构与上面类似,只是将 defer 换成了 async:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello, World!</h1>
<script async src="script1.js"></script>
<script async src="script2.js"></script>
</body>
</html>
由于网络波动,控制台输出可能是:
Script 1 Loaded!
Script 2 Loaded!
或者:
Script 2 Loaded!
Script 1 Loaded!
适用场景
async 适合独立的脚本模块,比如统计代码、广告脚本等。它们不需要依赖 DOM 结构,也不依赖其他脚本的执行结果。如果脚本之间有强依赖关系,使用 async 可能会导致运行时错误。
defer 与 async 的核心区别
虽然两者都允许并行下载且不阻塞 HTML 解析,但在执行时机和顺序上截然不同:
- 执行时机:
defer等待 HTML 解析完成;async下载完即执行。 - 执行顺序:
defer严格遵循文档顺序;async取决于下载完成时间。
在实际开发中,选择哪一个取决于需求:
- 如果脚本依赖 DOM 或其他脚本,优先选
defer。 - 如果脚本完全独立且希望尽快运行,可选
async。 - 如果没有特殊要求,现代浏览器默认推荐
defer,因为它更安全且不会破坏依赖链。
总结
合理使用这两个属性能显著提升页面加载体验。defer 兼顾了性能和执行顺序,是大多数场景下的首选;而 async 则用于处理那些对顺序不敏感的独立任务。掌握它们的底层行为,能帮助开发者写出更高效的 Web 应用。

