<script>标签对HTML文件解析过程的影响以及async和defer属性的应用

发布于:2025-07-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

在前端开发中,<script> 标签的 asyncdefer 属性会显著影响 JavaScript 脚本的加载和执行时机。下面结合示例代码,详细解析它们之间的区别:

1. 默认情况(无 async/defer

<script src="script.js"></script>
特性
  • 阻塞渲染:浏览器遇到 <script> 标签后,会立即暂停 HTML 解析,下载并执行脚本,直到脚本执行完毕才继续解析 HTML。
  • 执行顺序:按照标签在 HTML 中的出现顺序执行。
  • 适用场景:需要立即执行的脚本,或依赖于当前 DOM 的脚本。
示例
<!DOCTYPE html>
<html>
<head>
  <title>默认脚本加载</title>
</head>
<body>
  <div id="app">加载中...</div>
  
  <!-- 阻塞渲染,直到 script.js 下载并执行完毕 -->
  <script src="script.js"></script>
  
  <div>其他内容</div>
</body>
</html>

2. async 属性

<script async src="script.js"></script>
特性
  • 异步下载:脚本的下载与 HTML 解析同时进行,不会阻塞渲染。
  • 立即执行:脚本下载完成后,会立即暂停 HTML 解析并执行脚本。
  • 执行顺序不确定:多个 async 脚本的执行顺序取决于下载完成的时间,与标签顺序无关。
  • 适用场景:独立的、不依赖于其他脚本或 DOM 的异步脚本(如广告、分析工具)。
示例
<!DOCTYPE html>
<html>
<head>
  <title>async 脚本加载</title>
</head>
<body>
  <div id="app">加载中...</div>
  
  <!-- 不阻塞渲染,下载完成后立即执行 -->
  <script async src="analytics.js"></script>
  
  <div>其他内容</div>
</body>
</html>

3. defer 属性

<script defer src="script.js"></script>
特性
  • 异步下载:脚本的下载与 HTML 解析同时进行,不会阻塞渲染。
  • 延迟执行:脚本会在 HTML 解析完成后、DOMContentLoaded 事件触发前执行。
  • 执行顺序确定:多个 defer 脚本按标签在 HTML 中的出现顺序执行。
  • 适用场景:需要操作 DOM 或依赖于其他脚本的库(如 jQuery)。
示例
<!DOCTYPE html>
<html>
<head>
  <title>defer 脚本加载</title>
  <!-- defer 脚本会在 HTML 解析完成后按顺序执行 -->
  <script defer src="jquery.js"></script>
  <script defer src="app.js"></script>
</head>
<body>
  <div id="app">内容</div>
</body>
</html>

ps:

在浏览器解析 HTML 的过程中,defer 脚本的执行先于 DOMContentLoaded 事件

执行顺序原理

  1. defer 脚本的特性

    • 带有 defer 属性的脚本会与 HTML 解析并行下载(不阻塞解析)。
    • 下载完成后不会立即执行,而是等待整个 HTML 文档解析完成(即 DOM 构建完成)后,再按脚本在 HTML 中的顺序依次执行。
  2. DOMContentLoaded 事件的触发时机

    • 当整个 HTML 文档解析完成(DOM 树构建完毕),且所有 defer 脚本执行完成后,才会触发 DOMContentLoaded 事件。

示例验证

<!DOCTYPE html>
<html>
<head>
  <!-- defer 脚本:在 HTML 解析完成后执行 -->
  <script defer src="defer-script.js"></script>
  <script>
    // 监听 DOMContentLoaded 事件
    document.addEventListener('DOMContentLoaded', () => {
      console.log('DOMContentLoaded 事件触发');
    });
  </script>
</head>
<body>
  <h1>测试执行顺序</h1>
</body>
</html>
// defer-script.js
console.log('defer 脚本执行');
输出顺序:
defer 脚本执行
DOMContentLoaded 事件触发

结论

defer 脚本的执行先于 DOMContentLoaded 事件
defer 的设计初衷就是保证脚本在 DOM 就绪后、DOMContentLoaded 之前执行,适合需要操作 DOM 且依赖执行顺序的场景(如加载 jQuery 后再执行依赖它的代码)。

执行时机对比图

HTML 解析         |---------------------------->|
默认脚本          |    下载并执行    |
async 脚本        |      下载       | 执行 |
defer 脚本        |      下载       |       | 执行
DOMContentLoaded  |---------------------------->|

关键区别总结

特性 默认(无属性) async defer
是否阻塞渲染
下载时机 HTML 解析暂停 与 HTML 解析并行 与 HTML 解析并行
执行时机 下载后立即执行 下载完成后立即执行 HTML 解析完成后执行
执行顺序 按标签顺序 不确定(下载完成顺序) 按标签顺序
DOM 依赖 需确保 DOM 已加载 不依赖 DOM 可依赖 DOM

最佳实践

  1. 优先使用 defer:对于需要操作 DOM 或依赖于其他脚本的代码,使用 defer
  2. 使用 async 加载独立脚本:对于不依赖其他资源的异步脚本(如第三方库),使用 async
  3. 内联脚本避免使用 async/deferasyncdefer 仅对外部脚本(src 属性)有效,对内联脚本无效。

兼容性

  • asyncdefer 均支持现代浏览器及 IE10+。
  • 若需兼容 IE9 及以下版本,需使用默认加载方式或通过 JavaScript 动态加载脚本。

网站公告

今日签到

点亮在社区的每一天
去签到