async
和 defer
都是用于优化 HTML 中 <script>
标签加载行为的属性,核心区别在于脚本的执行时机和顺序控制。以下是详细对比:
1. 普通脚本(无属性)
<script src="script.js"></script>
-
行为:阻塞 HTML 解析,立即下载并执行脚本,执行完毕后继续解析 HTML。
-
影响:页面渲染延迟,用户体验较差。
2. async
(异步加载)
<script async src="script.js"></script>
-
下载:异步下载(不阻塞 HTML 解析)。
-
执行时机:脚本下载完成后立即执行(执行时仍会阻塞 HTML 解析)。
-
执行顺序:多个
async
脚本执行顺序不确定(先下载完成的先执行)。 -
适用场景:独立脚本(如广告、埋点统计),不依赖 DOM 或其他脚本。
-
注意:脚本可能在
DOMContentLoaded
事件之前或之后执行。
3. defer
(延迟执行)
<script defer src="script.js"></script>
-
下载:异步下载(不阻塞 HTML 解析)。
-
执行时机:脚本在 HTML 解析完成后、
DOMContentLoaded
事件前按顺序执行。 -
执行顺序:多个
defer
脚本严格按文档中的顺序执行。 -
适用场景:依赖 DOM 的脚本(如操作页面元素),或有依赖关系的多个脚本。
-
注意:保证脚本在 DOM 就绪后执行。
关键对比总结
特性 | async | defer |
---|---|---|
下载是否阻塞 HTML | 否(异步下载) | 否(异步下载) |
执行是否阻塞 HTML | 是(执行时阻塞) | 否(等 HTML 解析完才执行) |
执行时机 | 下载完成后立即执行 | HTML 解析完成后执行 |
多个脚本顺序 | 不保证顺序(先下载完先执行) | 严格按文档顺序执行 |
依赖 DOM | 不适用(可能 DOM 未就绪) | 适用(DOM 已就绪) |
脚本依赖关系 | 不适用(执行顺序不确定) | 适用(按顺序执行,可相互依赖) |
执行流程示意图
普通脚本: [下载] → [执行] → 继续解析 HTML
async: 异步下载 → [下载完成立刻执行] (可能中断 HTML 解析)
defer: 异步下载 → 等待 HTML 解析完成 → [按顺序执行]
最佳实践
-
使用
defer
:当脚本需要操作 DOM 或有依赖关系时(主流推荐)。 -
使用
async
:当脚本完全独立(如第三方分析),且不关心执行时机时。 -
避免同时使用
async
和defer
:部分浏览器会忽略defer
并退化为async
。
现代浏览器默认支持模块化脚本(
<script type="module">
),其行为类似defer
,可通过async
覆盖。