Skip to content

JavaScript 脚本的异步加载

异步加载脚本意味着脚本的下载过程不会阻塞 HTML 的解析

1. 脚本异步加载解决方式

Logo

Logo

1.1 defer 属性

Logo

defer 属性告诉浏览器立即下载脚本,但延迟执行

html
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>

1. 工作方式

  1. 并行下载:浏览器看到 defer 脚本,会立即在后台开始下载它,同时继续解析和渲染页面的其他部分。
  2. 延迟执行:脚本的执行会等到整个 HTML 文档被完全解析完毕(DOMContentLoaded 事件触发之前)才会开始。
  3. 顺序执行:如果有多个 defer 脚本,它们会按照在 HTML 中出现的顺序依次执行。script1.js 肯定会在 script2.js 之前执行。

2. 适用场景

  • 当脚本的执行依赖于整个 DOM 结构时(例如,需要操作页面上的所有元素)。
  • 当多个脚本之间有依赖关系,需要保证执行顺序时。

1.2 async 属性

Logo

async 属性告诉浏览器也立即下载脚本,但下载完成后会立即执行

html
<script src="scriptA.js" async></script>
<script src="scriptB.js" async></script>

1. 工作方式

  1. 并行下载:和 defer 一样,浏览器会立即在后台下载脚本,不阻塞页面渲染。
  2. 立即执行:一旦脚本下载完成,浏览器会立即暂停 HTML 解析,执行该脚本。执行完毕后,再继续解析 HTML。
  3. 乱序执行:多个 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. 适用场景

  • 当你需要根据某些条件或在特定时机才决定是否加载某个脚本时。例如,用户点击某个按钮后才加载一个功能模块。

总结与对比

为了让你更清晰地理解它们的区别,这里有一个简单的对比表格:

特性deferasync动态创建
下载时是否阻塞
执行时是否阻塞否(等到DOM解析完)是(下载完立即执行)是(下载完立即执行)
执行时机DOM 解析完毕后,DOMContentLoaded 之前下载完毕后立即执行下载完毕后立即执行
执行顺序按照在 HTML 中的顺序谁先下载完谁先执行(乱序)谁先下载完谁先执行(乱序)

经验法则:

  • defer 是首选:它兼顾了性能和可预测的执行顺序,是大多数场景下的最佳选择。
  • async 用于独立脚本:当脚本与你的主应用逻辑无关时使用。
  • 动态创建用于按需加载:当需要更灵活地控制脚本加载时机时使用。