Script 标签 defer 和 async

script 标签的 defer 和 async 属性区别及使用场景

问题

script 标签中 defer 和 async 的区别是什么?

解答

三种加载方式对比

<!-- 1. 普通 script:阻塞 HTML 解析 -->
<script src="script.js"></script>

<!-- 2. async:异步下载,下载完立即执行 -->
<script async src="script.js"></script>

<!-- 3. defer:异步下载,HTML 解析完后执行 -->
<script defer src="script.js"></script>

执行时序

普通 script:
HTML 解析 ──▶ 暂停 ──▶ 下载JS ──▶ 执行JS ──▶ 继续解析 ──▶ DOMContentLoaded

async:
HTML 解析 ─────────────────────────────────────▶ DOMContentLoaded
              ↓ 下载JS ──▶ 执行JS(随时可能打断解析)

defer:
HTML 解析 ──────────────────────────────────────▶ 执行JS ──▶ DOMContentLoaded
              ↓ 下载JS ─────────────────────────↑

实际使用示例

<!DOCTYPE html>
<html>
<head>
  <!-- defer:适合依赖 DOM 的脚本,按顺序执行 -->
  <script defer src="jquery.js"></script>
  <script defer src="app.js"></script> <!-- 保证在 jquery.js 之后执行 -->
  
  <!-- async:适合独立脚本,如统计、广告 -->
  <script async src="analytics.js"></script>
</head>
<body>
  <div id="app"></div>
</body>
</html>

验证执行顺序

<!DOCTYPE html>
<html>
<head>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      console.log('DOMContentLoaded');
    });
  </script>
  
  <script defer>
    console.log('defer 脚本执行');
    console.log('DOM 是否可用:', document.getElementById('app')); // 可用
  </script>
</head>
<body>
  <div id="app">Hello</div>
</body>
</html>

<!-- 输出顺序:
defer 脚本执行
DOM 是否可用: <div id="app">Hello</div>
DOMContentLoaded
-->

关键点

  • 下载阶段:async 和 defer 都不阻塞 HTML 解析
  • 执行时机:async 下载完立即执行;defer 等 HTML 解析完再执行
  • 执行顺序:async 不保证顺序;defer 按文档顺序执行
  • DOMContentLoaded:defer 脚本在该事件前执行;async 与该事件无关
  • 使用场景:defer 用于依赖 DOM 或有依赖关系的脚本;async 用于独立的第三方脚本