浏览器遇到 会暂停 html 解析直至 cssom 构建完成,因 js 可能依赖完整 cssom 获取样式;关键 css 应内联,非关键 css 用 preload + onload 切换或 media 属性绕过阻塞。

link rel="stylesheet" 为什么会让 HTML 解析暂停
浏览器遇到 <link rel="stylesheet"> 时,会暂停 HTML 解析,直到 CSSOM 构建完成。这不是 bug,是规范行为——因为 JS 可能通过 getComputedStyle() 或 offsetHeight 读取样式,而这些操作依赖完整的 CSSOM。如果解析继续推进,DOM 和 CSSOM 就可能不同步,导致布局或脚本出错。
常见错误现象:DOMContentLoaded 延迟、首屏白屏时间变长、内联脚本卡在 <link> 后面执行不了。
- 即使 CSS 文件返回 404,浏览器仍会等超时(通常数秒)才继续解析
- 多个
<link>是串行加载+解析的,不是并行构建 CSSOM - 使用
@import在 CSS 内部引入其他样式表,会进一步加深阻塞层级
如何让关键 CSS 快速就位又不阻塞解析
核心思路:把首屏渲染必需的样式(critical CSS)内联进 ,非关键样式延迟加载。这样 HTML 解析全程不等待网络请求,又能保证首屏样式可用。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 用工具如
critters或penthouse提取关键 CSS,避免手写遗漏 - 内联部分控制在 ~14KB 以内(避免触发 TCP 慢启动额外往返)
- 非关键 CSS 用
<link rel="preload" as="style" onload="this.rel='stylesheet'">加载,配合onload回调切换rel,避免无样式闪烁(FOUC) - 不要用
rel="prefetch"替代preload,它不提升优先级,也不触发onload
async/defer 对 CSS 无效,但 media 属性能绕过阻塞
async 和 defer 只对 <script></script> 生效,加在 <link> 上完全被忽略。但 media 属性是个例外:当值为浏览器当前不匹配的媒体类型(如 media="print"),该 CSS 不会阻塞渲染,只在对应条件满足时才加载解析。
使用场景:
-
<link rel="stylesheet" href="print.css" media="print">—— 完全不参与页面渲染流程 -
<link rel="stylesheet" href="mobile.css" media="(max-width: 768px)">—— 在桌面端初始解析时不加载不解析 - 后续可通过 JS 动态改
media值来激活,比如link.media = "all"
注意:media 匹配是同步判断的,不会引发重排,但切换后仍需重新下载和解析(除非已缓存)。
HTTP/2 和 preload 能加速,但不能消除阻塞本质
HTTP/2 的多路复用能让多个 <link> 并发下载,减少等待时间;preload 能提升资源优先级、提前触发请求。但这两者都不改变“CSSOM 构建完成前暂停 HTML 解析”这一根本约束。
容易踩的坑:
- 以为开了 HTTP/2 就可以随便放多个
<link>—— 实际上 CSSOM 构建仍是单线程、串行的,文件越多,解析越慢 - 在
里混用preload和普通<link>,导致资源竞争,关键 CSS 反而被挤到后面 - 用
fetch()+insertRule()动态注入样式 —— 这类 CSS 不参与 CSSOM 阻塞,但无法用于选择器匹配(如:hover)、不触发DOMContentLoaded等待,适用场景有限
真正影响 DOM 构建速度的,从来不是下载快慢,而是 CSSOM 构建是否必须同步完成。这个点一旦忽略,所有优化都只是在给瓶颈提速。










