CSS 不会阻塞 DOM 解析,但会阻塞 DOM 树遍历和渲染树构建;因 JS 引擎与渲染引擎需等待 CSSOM 就绪,故 document.querySelector 可能返回 null,DOMContentLoaded 时机也受 CSS 位置影响。

CSS 不会阻塞 DOM 解析,但会阻塞 DOM 树的遍历(即 JavaScript 对 DOM 的访问)和渲染树构建。 这个结论看似矛盾,实际源于浏览器内部线程分工与资源依赖关系——DOM 解析器可以继续推进,但 JS 引擎和渲染引擎会被 CSSOM 卡住。
为什么 document.querySelector 在 CSS 加载中可能返回 null
当脚本在 <head> 中执行,且其前有未完成加载的 <link rel="stylesheet"> 时,document.querySelector 可能查不到已解析但尚未“样式就绪”的元素。这不是 DOM 没建好,而是浏览器为保证样式计算一致性,将 JS 执行推迟到 CSSOM 构建完成。
- DOM 树仍在后台持续构建(字符流解析不受影响)
- 但 JS 引擎被挂起,等待
CSSOM就绪,否则getComputedStyle或伪类匹配等操作结果不可靠 - 这种延迟不是 DOM 解析阻塞,而是执行时机调度——
script标签本身不带async或defer时,会同步等待前面所有 CSS 加载完毕
DOMContentLoaded 触发时机取决于 CSS 和 script 的相对位置
这个事件代表 DOM 解析完成 + 所有同步脚本执行完毕,但它**不一定**等同于 DOM 树构建结束。如果页面中存在位于 <script> 前面的 <link rel="stylesheet">,那么 DOMContentLoaded 会等到该 CSS 加载并解析完才触发。
- 情况一:
<link>在<script>后 →DOMContentLoaded不等 CSS,只等 DOM 解析完 + 脚本执行完 - 情况二:
<link>在<script>前 → 浏览器必须确保脚本能看到最终样式,因此强制等待 CSSOM 完成 - Chrome 的预解析器(preload scanner)会提前下载 CSS,但不会提前解析或应用;它只是“下载快”,不改变执行依赖链
内联 CSS 与外部 CSS 的阻塞差异
内联 <style> 内容在 HTML 字节流中直接出现,解析器遇到即开始构建 CSSOM,不触发网络请求,因此对 JS 执行的延迟更可控;而外部 <link> 必须经历 DNS、TCP、TLS、HTTP 请求全过程,阻塞窗口更长且不可预测。
立即学习“前端免费学习笔记(深入)”;
-
<style>body{color:red}</style>:CSSOM 构建与 DOM 解析共享同一解析线程,无额外 I/O 延迟 -
<link href="main.css" rel="stylesheet">:触发独立网络请求,JS 执行需等响应 body 返回并解析完毕 - 注意:
rel="preload"+as="style"可提前拉取资源,但不改变执行顺序;仍需配合onload注入或media="print"切换才能绕过阻塞
真正容易被忽略的点:CSS 阻塞的是“可渲染性”和“样式可见性”,不是“结构可见性”
你可以在控制台看到 document.body 存在,也能读取 innerHTML,但调用 offsetHeight 或 getBoundingClientRect() 会卡住,直到 CSSOM 就绪。这是因为布局计算(Layout)依赖完整 CSSOM —— DOM 结构早已就位,只是浏览器拒绝在此时给出尺寸信息。
- DOM 树构建完成 ≠ 元素具备几何属性
- 渲染树(Render Tree)是 DOM 节点 + CSSOM 样式规则的交集,缺失任一都无法生成
- 哪怕只有一行
@import url("slow.css")在内联<style>中,也会触发额外请求并延续阻塞链



















