首屏白屏久但 DOMContentLoaded 早触发,是因为浏览器需完成 CSSOM 构建才能生成 Render Tree,而外部 <link rel="stylesheet"> 默认阻塞渲染;必须内联首屏关键 CSS(如 .hero-title、.cta-button),用 critters 等工具自动化提取并控制体积≤14KB,非关键 CSS 通过 media="print" onload 或动态插入异步加载。

为什么首屏白屏久但 DOMContentLoaded 很早就触发了
因为浏览器渲染不只等 DOM 构建完成,还必须等关键 CSSOM 构建完毕才能生成 Render Tree。外部 <link rel="stylesheet"> 默认阻塞 HTML 解析和绘制,哪怕 DOM 已就绪,页面仍白屏。
典型现象:main.css 在 Network 面板中 Initiator 显示为 parser,且耗时 >200ms;Performance 面板里 Recalculate Style 被卡在 CSS 加载之后。
-
@import绝对不能用——它强制串行加载,实测拖慢FCP300ms+ - 整站 CSS 打包进一个
app.css全量加载,会把后台管理页、弹窗组件等非首屏样式也拉进来,白白阻塞首屏 - 内联不是“把所有 CSS 塞进
<style>”,而是只保留首屏像素实际用到的规则(如.hero-title、.cta-button、导航栏固定高度)
怎么提取并内联首屏关键 CSS
手动提取容易漏伪类(.btn:hover)、媒体查询(@media (max-width: 768px))、动态 class(is-loading),必须靠工具链自动化。
- Vite 项目直接配
vite-plugin-critical,build 时自动生成 critical CSS 并注入index.html - Webpack 项目用
mini-css-extract-plugin分离出critical.css,再通过html-webpack-plugin的templateParameters注入内联内容 - 本地开发可用
puppeteer+critters:启动无头浏览器访问http://localhost:3000,抓取首屏 DOM,计算覆盖的 CSS 规则,输出内联<style>片段 - 内联体积建议控制在
14KB内(HTTP/2 单个 TCP 包的理想大小),超了反而增加 HTML 解析开销
非关键 CSS 怎么延迟加载又不破坏媒体查询
不能删掉 <link rel="stylesheet">,否则响应式断点、暗色模式等逻辑会失效。关键是让浏览器先跳过加载,等首屏稳定后再触发。
立即学习“前端免费学习笔记(深入)”;
- 用
media="print"初始化:<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">——浏览器默认不加载 print 样式,onload在资源就绪后才切换media,避免 FOUC - 更稳妥的做法是配合
requestIdleCallback或DOMContentLoaded后动态插入:link.rel = 'stylesheet',确保 DOM 就绪后再加载 - 避免在
<body>中写<link rel="stylesheet">,这会导致解析器反复回退重排
内联 CSS 的位置和内容限制
内联 <style> 必须紧贴 <meta charset> 之后,否则白屏问题无法缓解。顺序只能是:<meta charset="utf-8"> → <style> → 其他所有标签。
-
<style>中禁用@import和url()(比如字体或图片引用),它们会触发额外网络请求,破坏零阻塞目标 - 体积建议压到
1–5KB更安全——超过1KB会显著拖慢 TTFB,尤其在弱网下 - 服务端无法缓存内联样式,每次 HTML 更新都得重新传输全部内容,所以务必精简到“首屏像素生成所需最小集合”



















