defer 和 async 改变了外部 <script> 何时相对于 HTML 解析而下载和执行。两者都允许脚本并行下载,不阻塞解析器——区别在于执行时机。
html
Normal: parse──stop──[download+exec]──parse──────── (slowest)
async: parse──────────────────exec──parse──────── (runs whenever it arrives)
defer: parse───────────────────────────parse──exec (after parsing, before DOMContentLoaded)
async | defer | |
|---|---|---|
| 下载 | 并行 | 并行 |
| 执行 | 下载完成后立即执行 | HTML 完全解析后执行 |
| 保留顺序? | 否(谁先完成就谁先执行) | 是(文档顺序) |
| 阻塞解析器? | 仅在执行期间短暂阻塞 | 从不阻塞 |
defer — 用于接触 DOM 或相互依赖的脚本(你的应用代码)。它们在 DOM 存在后按顺序运行,在 DOMContentLoaded 前执行。async — 用于独立的第三方脚本,不依赖 DOM 或其他脚本(分析工具、广告)。顺序不重要,尽快运行它们。<script src="analytics.js" async></script> <!-- independent → async -->
<script src="app.js" defer></script> <!-- app logic, ordered → defer -->
**注意:**两个属性仅适用于外部脚本(src);它们在内联脚本上被忽略。ES 模块(type="module")默认被 defer。
使用 defer/async 防止脚本阻塞 HTML 解析,直接改进加载性能和首次内容绘制(FCP)。
选择正确的属性(对于有序依赖 DOM 的应用代码使用 defer,对于即发即弃的第三方脚本使用 async)可以避免加载缓慢和顺序依赖的 bug。