结论:rel="preload"预加载CSS必须配as="style"和onload动态改rel才能生效,否则仅下载不解析不应用;因preload设计为“提前取货”而非“立刻上架”,漏写as或onload会导致白屏、重复请求或CSP拦截。

直接说结论:用 rel="preload" 预加载关键 CSS 能显著提升首屏渲染速度,但仅加 rel="preload" 不生效——必须配 as="style" 和 onload 动态改 rel,否则它只下载不解析、不应用。
为什么 rel="preload" 加载的 CSS 一直不生效
浏览器把 rel="preload" 当作“提前取货”,不是“立刻上架”。哪怕你写了 <link rel="preload" href="critical.css" as="style">,它也只会进缓存,不会解析规则、不会参与渲染。这不是 bug,是设计如此。
- 漏写
as="style"→ 浏览器按as="fetch"处理,可能被 CSP 拦截,或降级为低优先级请求 - 没写
onload或 JS 注入逻辑 →rel始终是preload,样式永远不会挂载到 DOM - 同时存在另一个
<link rel="stylesheet" href="critical.css">→ Chrome 会发起两次请求(Network 面板里能看到两个 200),白占带宽
正确的最小可行写法(含降级)
必须把预加载标签和注入逻辑写在 HTML 的 <head> 里,且顺序不能错。最简健壮写法如下:
<link rel="preload" href="/css/critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="/css/critical.css"></noscript>
-
onload="this.onload=null;this.rel='stylesheet'"是核心:等资源加载完成,立刻转成可应用的样式表 -
this.onload=null防止某些浏览器(如旧版 Safari)重复触发 onload -
<noscript>不是可选项——Safari 11.1+、Chrome 50+ 支持 preload,但 IE 全系和部分老 WebView 完全忽略它,不加 fallback 会导致白屏 - 别用
document.write或动态appendChild插入 link,可能阻塞渲染或触发重排
什么时候该用,以及容易踩的坑
预加载只对「当前导航中马上要用」的资源有效,不是所有 CSS 都适合。
立即学习“前端免费学习笔记(深入)”;
- 适合场景:
critical.css(首屏按钮、导航栏、字体定义)、内联 SVG 中引用的外部样式、JS 动态插入前必须就位的动画类 - 不适合场景:整站通用 CSS、分页组件样式、
@import引入的子文件(浏览器无法提前发现,preload 无效) -
media属性能控制是否下载,但不能控制是否应用:media="(min-width: 768px)"表示小屏设备跳过请求;但一旦匹配,就会立即下载并缓存,后续切换视口也不会重新触发 - HTTP/2 推送(
Link: </critical.css>; rel=preload; as=style)已逐步弃用,现代项目应统一走前端preload+ JS 注入路径
怎么验证它真的起作用了
别只盯着 Network 面板看有没有 200——那只能说明下载成功。要确认它真正参与了首屏渲染:
- 打开 Elements 面板,搜索
critical.css,确认对应<link>的rel已变成stylesheet(不是卡在preload) - 用 Performance 面板录制首屏加载,对比开启前后:
Stylesheets解析时间是否明显前移、FCP 是否缩短 - 禁用 JavaScript 后刷新页面,检查是否仍能正常渲染首屏——如果白屏或错乱,说明
<noscript>没配或路径错了
最关键的细节往往藏在 as 和 onload 的组合里,少一个字符,整个预加载就失效。


















