defer 和 async 的区别
两者都是用于优化 JavaScript 文件的加载,防止阻塞 HTML 解析。但它们的行为有所不同,主要体现在加载顺序和执行时机上。
1. defer
特点
-
加载过程:
- 脚本文件会在 HTML 解析的同时并行加载,不阻塞 HTML 的解析。
-
执行顺序:
- 脚本的执行会在 HTML 完全解析(即 DOM 结构生成完成)后按顺序执行。
- 多个带
defer的脚本会按它们在 HTML 中的顺序依次执行。
-
依赖性:
- 适合脚本之间有依赖关系的情况,保证执行顺序。
典型应用场景
- 用于加载需要 DOM 解析完成后运行的脚本,比如页面逻辑代码。
- 如加载多个脚本文件,且需要按顺序执行(例如库依赖关系)。
代码示例
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>
<!-- 执行顺序:先执行 script1.js,后执行 script2.js -->
2. async
特点
-
加载过程:
- 脚本文件同样会在 HTML 解析的同时并行加载,不阻塞 HTML 的解析。
-
执行顺序:
- 脚本的执行会在文件加载完成后立即执行,不等待 HTML 解析完成,也不管脚本的加载顺序。即在
async脚本加载完成后立即执行 时,会阻塞 HTML 的解析。 - 多个带
async的脚本会根据文件加载完成的顺序执行,顺序不确定。
- 脚本的执行会在文件加载完成后立即执行,不等待 HTML 解析完成,也不管脚本的加载顺序。即在
-
依赖性:
- 不适合脚本之间有依赖关系的情况,可能导致执行顺序错误。
典型应用场景
- 用于加载独立的脚本,比如统计工具、广告脚本等,不依赖于其他脚本或 DOM。
代码示例
<script src="script1.js" async></script>
<script src="script2.js" async></script>
<!-- 执行顺序:由 script1.js 和 script2.js 的加载完成时间决定 -->
两者对比
| 特性 | defer | async |
|---|---|---|
| 加载方式 | 并行加载脚本文件,不阻塞 HTML 解析 | 并行加载脚本文件,不阻塞 HTML 解析 |
| 执行时机 | HTML 完全解析完成后(DOM 生成后)再执行 | 脚本加载完成后立即执行,不等待 HTML 解析 |
| 执行顺序 | 按 HTML 中的顺序执行 | 根据加载完成的先后顺序执行,顺序不确定 |
| 脚本依赖性 | 支持脚本依赖关系,保证执行顺序 | 不支持脚本依赖关系 |
| 适用场景 | 需要 DOM 结构、脚本有依赖关系 | 独立脚本,无依赖关系 |
总结使用建议
-
使用
defer:- 如果脚本需要在 HTML 解析完成后运行,并且多个脚本之间存在依赖关系(例如库和业务代码)。
- 推荐给页面逻辑代码加上
defer,确保脚本按顺序执行,并且不会影响首屏渲染。
<script src="main.js" defer></script> -
使用
async:- 如果脚本是独立的,不依赖其他脚本或 DOM,可以用
async优化加载速度。 - 常见场景:第三方统计工具、广告、用户行为分析等。
<script src="analytics.js" async></script> - 如果脚本是独立的,不依赖其他脚本或 DOM,可以用
-
不要同时使用
defer和async:- 两者功能互斥,浏览器会忽略
defer属性,仅按async行为处理。
- 两者功能互斥,浏览器会忽略
普通 <script> 标签的对比
| 加载方式 | HTML 解析是否阻塞 | 脚本加载是否阻塞 HTML 解析 | 脚本执行时机 | 执行顺序 |
|---|---|---|---|---|
普通 script | 阻塞 | 阻塞 | 立即执行(加载完成后) | 按 HTML 顺序 |
defer | 不阻塞 | 并行加载 | DOM 解析完成后再执行 | 按 HTML 顺序 |
async | 不阻塞 | 并行加载 | 加载完成后立即执行 | 加载完成顺序(不确定) |