JS 文件对 DOM 和 CSSOM 的阻塞

JavaScript 在什么情况下会阻塞 DOM 和 CSSOM 的构建,以及如何避免

问题

页面加载过程中,JS 文件是否一定会阻塞 DOM 和 CSSOM 的构建?

解答

答案是:不一定。

会阻塞的情况

1. JavaScript 文件放置在 head 标签内部

当 JavaScript 文件被放置在 head 标签内部时,浏览器会先加载并执行 JavaScript 文件,然后才继续解析 HTML 文档。如果 JavaScript 文件过大或服务器响应时间过长,页面会一直处于等待状态,阻塞 DOM 和 CSSOM 的构建。

<head>
  <script src="large-script.js"></script>
  <!-- 页面会等待脚本加载和执行完成 -->
</head>

2. JavaScript 代码修改了 DOM 结构

在 JavaScript 执行时,如果对 DOM 结构进行了修改,浏览器需要重新计算布局(reflow)和重绘(repaint),这个过程会阻塞 DOM 和 CSSOM 的构建。

不会阻塞的情况

1. 使用 async 属性

异步加载 JavaScript 文件,脚本的下载和执行与其他工作同时进行。脚本加载完成后立即执行,不保证执行顺序。

<script src="script.js" async></script>

2. 使用 defer 属性

浏览器立即下载脚本文件,但延迟到文档解析完成后才执行。脚本按照在页面中出现的顺序执行。

<script src="script1.js" defer></script>
<script src="script2.js" defer></script>
<!-- script1 会在 script2 之前执行 -->

3. 使用 Web Workers

Web Workers 运行在后台线程,不会阻塞 DOM 和 CSSOM 的构建,可以利用多核 CPU 提高执行速度。

const worker = new Worker('worker.js');
worker.postMessage('start');

关键点

  • 普通 script 标签在 head 中会阻塞 DOM 解析,应放在 body 底部或使用 async/defer
  • async 适合独立脚本(如统计代码),下载完立即执行,不保证顺序
  • defer 适合依赖 DOM 的脚本,按顺序执行且在 DOMContentLoaded 之前完成
  • 修改 DOM 会触发 reflow 和 repaint,应尽量批量操作减少性能损耗
  • Web Workers 适合处理耗时计算,不阻塞主线程