CSS 放 head 仍闪烁是因为外部样式表默认不阻塞 HTML 解析,导致 CSSOM 未就绪前页面先渲染。关键在确保关键样式阻塞渲染:用 rel=”preload” as=”style” 提前加载并配合 onload 切换为 stylesheet,禁用 @import 和 JS 动态插入,构建时输出静态 CSS 文件。

为什么 CSS 放在里还是闪?
因为浏览器解析 HTML 时,遇到外部 <link rel="stylesheet"> 会发起网络请求,但默认不阻塞后续 HTML 解析(除非有 media 匹配或disabled),而一旦样式表加载完成、CSSOM 构建完毕,才会触发重排重绘——这中间的空白期就是闪烁来源。关键不是“放没放头部”,而是“是否真正阻塞了渲染直到关键样式就绪”。
用 rel="preload" 提前拉取关键 CSS
把核心样式文件(如重置、布局、首屏组件)用 <link rel="preload"> 声明在 <head> 最前面,强制浏览器高优先级下载,且不执行,等后续 <link rel="stylesheet"> 出现时直接复用已加载资源。
-
rel="preload"必须配as="style",否则不会被识别为样式资源 - 不能省略
onload回调,否则加载完不会自动应用;典型写法:<link rel="preload" href="critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> - IE 不支持
preload,需配合rel="stylesheet"降级:同一文件写两遍,第二遍无preload属性
避免 @import 和动态插入样式表
@import在 CSS 内部使用会引发串行加载,延迟关键 CSS 就绪时间;JS 动态创建 <link> 或<style>更糟——它完全绕过 HTML 解析器预加载机制,必然导致 FOUC(Flash of Unstyled Content)。
- 所有
@import都应转成<link>标签,按依赖顺序排列 - JS 中插入样式,只适用于非关键、可延迟的模块(如暗色主题切换),首屏样式绝不能靠 JS 注入
- Webpack/Vite 等构建工具若配置了
style-loader,默认就是动态插入,生产环境务必改用mini-css-extract-plugin输出静态.css文件
内联首屏关键 CSS + 外链剩余样式
把首屏渲染必需的 CSS(通常 <head> 的 <style> 标签里,确保零网络延迟;其余样式仍用 <link rel="stylesheet"> 外链,避免阻塞后续资源加载。
立即学习 “ 前端免费学习笔记(深入)”;
- 内联内容必须精简:删注释、压缩、剔除未用选择器(可用 PurgeCSS)
- 服务端渲染(SSR)场景下,需在生成 HTML 时动态注入对应路由的关键 CSS,不能写死
- 注意 HTTP 缓存失效问题:内联 CSS 随 HTML 更新,外链 CSS 可长期缓存,二者版本需解耦
真正难的不是“放哪里”,是判断哪些 CSS 算“关键”——它取决于设备尺寸、用户登录态、A/ B 实验分组,甚至当前路由参数。这个边界一旦划错,要么白内联(仍闪烁),要么过度内联(HTML 体积暴涨拖慢首字节)。