关键路径CSS必须内联进<head>,否则浏览器卡在CSSOM构建阶段导致白屏;critters在Vite中需预渲染生成真实首屏DOM快照才能提取成功,纯CSR默认失败,须配合vite-plugin-prerender或SSR并显式声明路由。

关键路径 CSS 必须内联进 <head>,否则浏览器卡在 CSSOM 构建阶段,白屏时间直接等于 CSS 加载耗时——这不是优化项,是首屏渲染的硬性前提。
critters 提取失败:为什么 Vite 里装了插件却没生成 <style data-critters>
根本原因是 critters 没拿到真实首屏 DOM 快照。它不靠正则扫 CSS,而是解析 HTML 后模拟浏览器构建 CSSOM,反向追踪哪些规则参与了首屏渲染计算。
- 纯 CSR(客户端渲染)项目默认会失败:构建时
dist/index.html中<body>是空的,critters 无 DOM 可分析 - 必须开启预渲染:用
vite-plugin-prerender或vite-plugin-ssr生成含首屏内容的静态 HTML;或配build.ssr: true(需配套服务端入口) - 若首屏依赖路由异步组件(如
defineAsyncComponent),得在prerenderRoutes显式声明,例如['/', '/product'],否则 critters 不知道该抓哪块 DOM -
vite-plugin-critters比手动加critters()更稳——它接管 HTML 构建流程,自动处理<link rel="stylesheet">移除和<style>注入
penthouse 提取漏样式:超时、JS 未就绪、响应式断点错配
penthouse 靠 Puppeteer 真实加载页面,精度高但极易因“页面没稳定”而漏提关键规则,比如 .menu[open] 或动态插入的卡片样式。
- 超时不是设小了,而是页面没真正 ready:SPA 常需等数据请求完成、Vue/React mount 结束,建议加
--timeout 60000并配合--wait-for "window.__APP_READY__ === true" - 设备视口必须匹配真实首屏:用
--viewport-width 375 --viewport-height 667模拟 iPhone SE,别用默认 1200×800——否则@media (max-width: 768px)下的导航栏规则压根不会被触发 - penthouse 只返回纯 CSS 字符串,你得手动包裹成
<style type="text/css">...</style>,漏掉type="text/css"会导致旧版 Safari 忽略 - 它不识别
data-critters="skip"这类标记,动态 JS 插入的样式表(如 analytics.css)需提前在 HTML 中移除或禁用
内联后仍 FOUC 或 FCP 升高:体积、位置、重复加载三重坑
内联本身不保证效果,塞错内容或放错位置反而拖慢首屏。Lighthouse 报 “Eliminate render-blocking resources” 但你已内联?大概率踩了下面任意一条。
立即学习“前端免费学习笔记(深入)”;
- 体积超限:
<style data-critters>必须 ≤ 14KB(HTTP/2 下建议 ≤ 10KB),否则阻塞 HTML 解析;critters 默认不限制,得手动配maxSize: 10240 - 位置错误:必须放在
<head>最顶部,且在所有<link rel="stylesheet">之前——若被<meta charset>或其他<script>挡住,浏览器仍会暂停渲染 - 原始
<link rel="stylesheet">没删干净:critters 不自动移除,得用插件配置inlineFonts: false或手动html.replace(/<link rel="stylesheet"[^>]*>/g, '') - @import 规则被忽略:若主 CSS 里有
@import 'base.css',critters 不下载也不解析base.css,关键规则必须平铺到入口 CSS 文件中
真正难的不是选 critters 还是 penthouse,而是确保提取时看到的 DOM 和用户打开页面时一模一样——Vite 的构建快照、penthouse 的运行快照、SSR 输出的 HTML,三者状态稍有差异,提取结果就可能漏掉 :hover、[open] 或某条媒体查询下的字体大小。



















