浏览器允许 写在 ,但会导致FOUC、布局重排和解析中断;DOMContentLoaded 等 CSSOM 完成因 CSS 是渲染阻塞资源,而 JS 默认仅解析阻塞;推荐用 media 属性或 onload 动态激活样式,而非挪动位置。
必须写在 里不是“必须写在 ”,而是写在 中间会导致不可控的 FOUC(Flash of Unstyled Content)和布局重排,浏览器会主动阻塞渲染直到 CSSOM 构建完成。HTML 规范允许 出现在 ,但实际效果极差——现代浏览器遇到 body 内的样式表时,会暂停解析 HTML、回退并同步加载该 CSS,造成严重性能抖动。
DOMContentLoaded 为何等 CSS 而不等 JS因为 CSS 是渲染阻塞资源(render-blocking),而 JS 默认是解析阻塞资源(parser-blocking)。关键区别在于:JS 执行可能修改 DOM 或 CSSOM,所以浏览器必须等 CSSOM 就绪后,才能安全执行后续 JS(尤其当 JS 读取 getComputedStyle 或 offsetHeight 时)。这意味着:
在 中,会触发 CSS 加载 → 阻塞 HTML 解析 → 阻塞 DOMContentLoaded
放在 
async/defer,也会阻塞解析,但它不参与 CSSOM 构建 底部,它不会阻塞 CSS 加载,但一旦执行时访问样式相关属性,仍需等待 CSSOM 完成 放在 会发生什么实测 Chrome/Firefox 会立即中断 HTML 解析,发起 CSS 请求,并将该 CSS 视为“关键样式表”强制同步加载——即使它本应是非关键资源。典型问题包括:
只对 中声明的资源有效Hello World
media 和 onload 回调想避免阻塞又保持样式可用性?别挪位置,改用特性控制加载时机:
media="print" 声明非关键样式,浏览器初始不加载,打印时才 fetch(可配合 onload 切换为 all) 实现异步加载 + 后续激活onload,需 fallback 用 onreadystatechange
rel="preload" + as="style":它只提前请求,不自动应用,仍需手动插入 或切换 link 的 media
CSS 加载机制的核心不是“放哪”,而是“何时构建 CSSOM”。 是约定俗成的安全区,不是语法限制;真正容易被忽略的是:哪怕你把 放对了位置,只要没配好 media、preload 或 HTTP 缓存头,它照样会成为首屏瓶颈。