defer 和 async 的区别

1. 核心区别

<script> 标签中,deferasync 都是用于异步加载脚本的属性,主要区别在于执行时机

属性下载时机执行时机执行顺序是否阻塞 HTML 解析
<无属性>立即下载 (阻塞)下载完立即执行按文档顺序 (下载和执行都阻塞)
async异步下载 (不阻塞)下载完立即执行不保证 (谁先下完谁执行) (仅执行时阻塞)
defer异步下载 (不阻塞)HTML 解析完成后DOMContentLoaded 之前保证 (按文档顺序) (完全不阻塞)

2. 详细解析

(1) async (异步)

  • 行为:浏览器看到 async 脚本,会开启线程异步下载,不阻塞 HTML 解析。但是一旦下载完成,会立即暂停 HTML 解析来执行脚本。
  • 场景:适用于完全独立的脚本,不依赖其他脚本,也不被其他脚本依赖。例如:埋点统计广告联盟

(2) defer (推迟)

  • 行为:浏览器异步下载,不阻塞 HTML 解析。下载完成后不立即执行,而是等到整个 HTML 解析完毕(但在 DOMContentLoaded 事件触发之前)才执行。
  • 场景:适用于依赖 DOM 元素有依赖关系的脚本。例如:业务逻辑代码框架库

3. 面试高频问题

Q: 推荐使用哪种方式?

推荐使用 defer。 因为它最接近我们将 <script> 放在 <body>底部的效果,既能并行下载提高速度,又能保证执行顺序,且不阻塞页面渲染。

Q: 多个 defer 脚本的执行顺序?

按照它们在 HTML 中出现的顺序执行。

Q: 多个 async 脚本的执行顺序?

不确定。谁先下载完谁先执行。所以如果 b.js 依赖 a.js,千万不能用 async

Q: 动态创建的 script 标签默认是什么?

document.createElement('script') 创建的脚本,默认是 async 的(即异步加载并尽快执行)。如果需要顺序执行,需设置 .async = false