关键CSS必须用预加载并配合onload切换或内联,否则不生效;浏览器遇会阻塞HTML解析直至CSSOM构建完成,导致FCP延迟。

关键CSS必须用 <link rel="preload"> 提前拉取,但仅 preload 不够,不配合 onload 切换或内联,它根本不会生效。
为什么 <link rel="stylesheet"> 会拖慢首屏
浏览器遇到 <link rel="stylesheet"> 会立即暂停 HTML 解析,等 CSSOM 构建完才继续渲染。哪怕文件只大 50KB、CDN 延迟 80ms,首屏就可能卡住 300ms 以上。这不是网络问题,是浏览器的强制阻塞行为。
常见错误是把所有 CSS 合成一个 main.css 然后 <link rel="stylesheet" href="main.css"> —— 这等于把首屏和非首屏样式绑死,加载即阻塞,完全没利用浏览器并行能力。
- 关键路径上每多一个阻塞资源,FCP(首次内容绘制)就延迟一次 RTT
- 字体、图标、布局类样式通常在首屏渲染前就需就位,但它们常藏在
@import或深层 CSS 文件里,浏览器发现太晚 - Webpack 默认 splitChunks 生成的 CSS chunk,若没显式标记为“关键”,也不会被优先加载
<link rel="preload"> 加载 CSS 的正确写法
必须同时满足三个条件:指定 as="style"、声明 onload 回调、避免重复引入。漏任一环,资源要么不复用,要么不应用,要么触发两次请求。
立即学习“前端免费学习笔记(深入)”;
正确示例:
<head>
<!-- 内联最小关键样式(可选但推荐)-->
<style>body{margin:0}.header{font-size:1.2rem}</style>
<p><!-- 预加载关键 CSS -->
<link rel="preload" href="/css/critical.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/critical.css"></noscript>
</head>-
as="style"是硬性要求:没有它,浏览器不会将 preload 资源匹配到后续rel="stylesheet"请求,导致重复下载 -
onload="this.rel='stylesheet'"必须存在:preload 只下载不执行,靠这个回调把 link 变成真实样式表 - 加
<noscript></noscript>回退:Safari 旧版本或禁用 JS 时,仍能加载样式 - 不要写
media="print"然后 onload 切media="all":这在 Chrome 120+ 已失效,且 Safari 对 media 切换有竞态 bug
如何识别和分离“关键 CSS”
关键 CSS 不是“体积小的 CSS”,而是“首屏渲染必需的那部分规则”。它和页面结构强相关,不能靠人工猜,也不能全量提取。
- 用
critters(Vite/webpack 插件)或penthouse在构建时自动提取:传入首屏 HTML 快照 + 所有 CSS,输出最小关键集 - 手动验证方法:打开 DevTools → Network → Disable cache → 刷新,看
critical.css是否在 FCP 前完成下载且无 warning - 警惕
@import:它串行加载、无法被 preload,必须转成独立<link>或内联 - 字体必须单独 preload:比如
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>,否则即使 CSS 已加载,文字仍会闪动
Safari 和低版本 Chrome 的兼容细节
Chrome 73+、Firefox 64+、Edge 79+ 原生支持 rel="preload";Safari 11.1+ 支持,但对 onload 的执行时机更敏感。
- Safari 13–15.6:
onload属性有时不触发,需改用addEventListener('load', ...) - 稳妥写法(兼容所有主流浏览器):
<link rel="preload" href="/css/critical.css" as="style" id="critical-css">
<script>
const link = document.getElementById('critical-css');
link.addEventListener('load', () => { link.rel = 'stylesheet'; });
if (link.readyState === 'complete') link.rel = 'stylesheet';
</script>真正容易被忽略的点是:preload 的资源必须和最终 rel="stylesheet" 的 URL 完全一致(含查询参数),否则缓存不复用;而 Webpack 构建出的带 hash 的 CSS 名,如 critical.abc123.css,必须确保 preload 的 href 和最终插入的 href 是同一个字符串 —— 这需要构建时注入,不能硬编码。


















