JavaScript 脚本的异步加载
异步加载脚本意味着脚本的下载过程不会阻塞 HTML 的解析。
1. 脚本异步加载解决方式


1.1 defer 属性

defer 属性告诉浏览器立即下载脚本,但延迟执行。
html
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>1. 工作方式
- 并行下载:浏览器看到
defer脚本,会立即在后台开始下载它,同时继续解析和渲染页面的其他部分。 - 延迟执行:脚本的执行会等到整个 HTML 文档被完全解析完毕(
DOMContentLoaded事件触发之前)才会开始。 - 顺序执行:如果有多个
defer脚本,它们会按照在 HTML 中出现的顺序依次执行。script1.js肯定会在script2.js之前执行。
2. 适用场景
- 当脚本的执行依赖于整个 DOM 结构时(例如,需要操作页面上的所有元素)。
- 当多个脚本之间有依赖关系,需要保证执行顺序时。
1.2 async 属性

async 属性告诉浏览器也立即下载脚本,但下载完成后会立即执行。
html
<script src="scriptA.js" async></script>
<script src="scriptB.js" async></script>1. 工作方式
- 并行下载:和
defer一样,浏览器会立即在后台下载脚本,不阻塞页面渲染。 - 立即执行:一旦脚本下载完成,浏览器会立即暂停 HTML 解析,执行该脚本。执行完毕后,再继续解析 HTML。
- 乱序执行:多个
async脚本的执行顺序是不可预测的。哪个脚本先下载完,哪个就先执行。scriptB.js完全有可能在scriptA.js之前执行。
2. 适用场景
- 当脚本是完全独立的,不依赖于 DOM,也不依赖于其他任何脚本时。
- 例如:网站分析工具、广告脚本、或一些独立的第三方库。
1.3 动态创建 <script> 标签
我们还可以使用 JavaScript 来动态创建一个 <script> 元素并将其添加到文档中,这也是一种实现异步加载的方式。
js
// 创建一个新的 <script> 标签
const script = document.createElement('script');
script.src = 'my-dynamic-script.js';
// 将其添加到 <head> 或 <body>
document.head.appendChild(script);1. 工作方式
- 这种方式创建的脚本,其行为在现代浏览器中类似于
async。脚本的下载和执行都是异步的,下载完成后会立即执行,并且不会保证执行顺序。 - 它不会阻塞页面的解析和渲染。
2. 适用场景
- 当你需要根据某些条件或在特定时机才决定是否加载某个脚本时。例如,用户点击某个按钮后才加载一个功能模块。
总结与对比
为了让你更清晰地理解它们的区别,这里有一个简单的对比表格:
| 特性 | defer | async | 动态创建 |
|---|---|---|---|
| 下载时是否阻塞 | 否 | 否 | 否 |
| 执行时是否阻塞 | 否(等到DOM解析完) | 是(下载完立即执行) | 是(下载完立即执行) |
| 执行时机 | DOM 解析完毕后,DOMContentLoaded 之前 | 下载完毕后立即执行 | 下载完毕后立即执行 |
| 执行顺序 | 按照在 HTML 中的顺序 | 谁先下载完谁先执行(乱序) | 谁先下载完谁先执行(乱序) |
经验法则:
defer是首选:它兼顾了性能和可预测的执行顺序,是大多数场景下的最佳选择。async用于独立脚本:当脚本与你的主应用逻辑无关时使用。- 动态创建用于按需加载:当需要更灵活地控制脚本加载时机时使用。