在前端开发中,<script>标签的async和defer属性均用于优化脚本加载与页面渲染的关系,避免脚本阻塞页面解析,但二者在执行时机、加载顺序、适用场景上存在关键区别。下面从核心概念、工作流程、对比分析和实践建议四个维度详细说明:
一、核心概念:为什么需要 async/defer?
默认情况下(无async/defer),浏览器解析HTML时遇到<script>标签会:
- 暂停HTML解析(阻塞渲染);
- 下载脚本文件;
- 下载完成后立即执行脚本;
- 执行结束后才恢复HTML解析。
这种"阻塞"会导致页面加载缓慢(尤其是脚本体积大或网络差时)。
async和defer的核心作用是:让脚本下载与HTML解析并行进行,仅在特定时机执行脚本,减少对页面渲染的阻塞。
二、async 属性:"异步加载,加载完立即执行"
async(异步)的核心逻辑是"加载优先,执行不等待",适用于不依赖其他脚本/DOM、也不被其他脚本依赖的独立脚本(如统计脚本、广告脚本)。
工作流程
- 浏览器解析HTML时,遇到
<script async src="xxx.js">,会并行下载脚本(不暂停HTML解析); - 脚本下载完成后,会立即暂停HTML解析,执行该脚本;
- 脚本执行结束后,恢复HTML解析。
关键特性
- 执行时机不确定:脚本的执行顺序完全取决于"下载速度",下载快的先执行(即使脚本在HTML中顺序靠后,也可能先执行);
- 不保证顺序 :若多个
async脚本存在依赖关系(如A脚本依赖B脚本),可能出现A先执行、B后执行的错误; - 执行时阻塞解析:脚本下载完成后会"插队"执行,此时仍会暂停HTML解析(但下载阶段不阻塞)。
示例
js
<!-- 脚本1体积小,脚本2体积大 -->
<script async src="small.js"></script>
<script async src="large.js"></script>
<!-- 实际执行顺序:small.js可能比large.js先执行(因下载快),与HTML中顺序无关 -->
三、defer 属性:"异步加载,解析完再执行"
defer(延迟)的核心逻辑是"加载并行,执行排队",适用于依赖DOM结构、或依赖其他脚本的脚本(如初始化页面的脚本、依赖jQuery的插件)。
工作流程
- 浏览器解析HTML时,遇到
<script defer src="xxx.js">,会并行下载脚本(不暂停HTML解析); - 脚本下载完成后,不立即执行,而是等待HTML解析完全结束 (即触发
DOMContentLoaded事件前); - 所有
defer脚本按HTML中的声明顺序依次执行。
关键特性
- 执行时机固定:必须等到HTML解析完才执行,不会"插队"阻塞解析;
- 保证顺序 :多个
defer脚本的执行顺序与它们在HTML中的顺序完全一致(即使后声明的脚本先下载完,也会等待前一个执行); - 依赖安全 :若脚本A依赖脚本B,只需在HTML中让B排在A前面(均加
defer),即可确保B先执行。
示例
js
<!-- 脚本1依赖脚本2,脚本2体积大 -->
<script defer src="large.js"></script> <!-- 声明在前 -->
<script defer src="small.js"></script> <!-- 声明在后 -->
<!-- 实际执行顺序:large.js先执行,small.js后执行(与声明顺序一致),且都在HTML解析完后执行 -->
四、async vs defer 核心对比
为了更清晰区分,下表从6个关键维度对比二者差异:
| 对比维度 | async | defer |
|---|---|---|
| 下载与解析关系 | 下载与HTML解析并行 | 下载与HTML解析并行 |
| 执行时机 | 下载完成后立即执行 | HTML解析完全结束后执行 |
| 执行顺序 | 不保证(按下载速度排序) | 保证(按HTML声明顺序排序) |
| 是否阻塞解析 | 执行时阻塞,下载时不阻塞 | 下载和执行时均不阻塞 |
| 依赖支持 | 不支持(无法依赖其他脚本) | 支持(可按顺序依赖其他脚本) |
| 适用场景 | 独立脚本(统计、广告、监控) | 依赖DOM/其他脚本(初始化、插件) |
五、与默认脚本(无属性)的对比
| 脚本类型 | 下载阶段是否阻塞HTML解析 | 执行阶段是否阻塞HTML解析 | 执行顺序 |
|---|---|---|---|
| 默认脚本(无属性) | 是(暂停解析,先下载) | 是(执行时继续暂停) | 按HTML顺序 |
| async 脚本 | 否(并行下载) | 是(执行时暂停) | 按下载速度 |
| defer 脚本 | 否(并行下载) | 否(解析完再执行) | 按HTML顺序 |
六、实践建议
- 优先用 defer 的场景:
-
- 脚本依赖DOM(如操作
<div id="app">),需等HTML解析完才能执行; - 脚本间有依赖关系(如A依赖B,B依赖jQuery),需保证执行顺序;
- 页面核心功能脚本(如初始化交互、渲染内容)。
- 脚本依赖DOM(如操作
- 优先用 async 的场景:
-
- 脚本完全独立(不依赖任何DOM/其他脚本,也不被其他脚本依赖);
- 非核心脚本(如百度统计、Google Analytics、广告脚本),早执行或晚执行不影响页面功能。
- 注意事项:
-
async/defer仅对外部脚本(带src属性)有效 ,对内嵌脚本(<script>...</script>)无效;- 若同时设置
async和defer,浏览器会忽略defer,仅按async规则执行; - 即使使用
defer,脚本也应避免在DOMContentLoaded事件后才执行关键逻辑(因defer已确保在该事件前执行)。