浏览器通过渲染流水线各阶段的依赖关系自然协同:脚本执行暂停dom解析,但样式计算是否阻塞取决于cssom就绪状态;firefox强制阻塞后续脚本直至cssom完成,webkit仅在脚本读取未就绪样式时才阻塞;预解析器并行下载资源但不执行,真正性能瓶颈在于同步依赖链而非事件循环调度。

浏览器不是靠“调度事件循环”来主动平衡脚本执行和样式计算的,而是由渲染流水线中多个阶段的依赖关系和引擎行为自然形成的协同机制。关键不在于循环调度,而在于解析、执行、样式计算这几个阶段何时发生、谁等谁、谁阻塞谁。
脚本执行会暂停 DOM 解析,但不一定停样式计算
当 HTML 解析器遇到 <script></script> 标签(尤其未加 defer 或 async 的内联或同步外部脚本),它会立即暂停 DOM 构建,等待脚本下载(如果是外部)、解析并执行完毕。此时:
- DOM 树构建中断,后续 HTML 不再继续解析;
- 已解析的 DOM 节点可触发样式计算(CSSOM + DOM → Render Tree),但前提是所需 CSS 已就绪;
- 如果脚本里调用
getComputedStyle()或访问offsetHeight等布局信息,浏览器会强制触发样式计算甚至布局(reflow),此时必须等 CSSOM 完全构建完成——这正是样式表可能反向阻塞脚本的原因。
样式表加载会间接影响脚本执行时机
样式表本身不阻塞 DOM 解析(<link rel="stylesheet"> 是异步发起请求的),但它会阻塞后续脚本的执行——尤其是那些依赖样式信息的脚本。不同引擎策略略有差异:
- Firefox:只要 CSS 文件还在加载或解析中,所有后续脚本一律挂起,直到 CSSOM 构建完成;
- WebKit(Chrome/Safari):仅当脚本实际读取了尚未解析完的样式(如访问
element.offsetWidth),才临时阻塞并等待 CSSOM 就绪; - 无论哪种,
<style></style>内联样式都由 HTML 解析器直接处理,不引发网络等待,因此几乎不造成额外延迟。
现代浏览器用预解析和并行加载缓解阻塞
虽然主线程被脚本占用时 DOM 解析暂停,但浏览器会启动一个“预解析器”(preload scanner)在后台做两件事:
- 扫描剩余 HTML,提前发现
<link rel="stylesheet">、<script src></script>、<img>等资源并发起并行请求; - 这些请求不受主线程阻塞影响,能充分利用空闲连接快速下载资源;
- 但预解析器不修改 DOM,也不执行脚本或计算样式——它只负责“找资源+发请求”,真正的解析与执行仍由主解析器和 JS 引擎按序完成。
真正影响性能的是“同步依赖链”而非事件循环调度
所谓“平衡”,本质是避免不必要的同步等待。例如:
- 把
<script></script>放在











