extractCritical 返回空字符串的三大原因是:未下载远程样式表、只扫描静态class而忽略动态class、@import未被展开;应显式传入CSS字符串并限制分析范围。

extractCritical 返回空字符串怎么办
这不是代码写错,而是工具根本没看到真实样式规则。常见原因有三个:extractCritical 默认不下载远程 <link rel="stylesheet">,如果路径 404、缺 <base> 或跨域,就解析不出任何内容;SSR 输出的 HTML 含动态 class(如 class="btn is-loading"),但工具只扫描模板里的静态 class;@import 没被 postcss-import 展开,critters 直接跳过整条规则。
实操建议:
- 用
fs.readFileSync('./dist/style.css', 'utf8')把 CSS 文件读成字符串,显式传给extractCritical的css参数,绕过路径解析逻辑 - 确保传入的 HTML 字符串已内联基础样式,或至少是 Puppeteer 截图后生成的本地完整快照
- 加
pruneSource: true和include: '#app',限制分析范围,避免扫进node_modules里未用的样式
critical CLI 提取后 FOUC 更严重
这不是 Critical CSS 本身的问题,而是 DOM 里同时存在内联块和原始 <link rel="stylesheet"> 标签。浏览器先按内联样式画一次,等外部 CSS 加载完再重绘一遍,造成两次渲染。
更糟的是,如果内联块里含 @font-face 或 url(/img/hero.jpg),这些会同步触发网络请求,卡死 CSSOM 构建。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 必须从 HTML 中彻底移除所有被提取覆盖的
<link rel="stylesheet">标签——不能只靠内联就以为万事大吉 - 禁止在 Critical CSS 块里写
@import、@font-face、任何含url()的声明;这些该留在非关键 CSS 中,用rel="preload" as="style"控制加载时机 - 内联的
<style>必须放在<head>最顶部,且type="text/css"不可省略(旧版 Safari 会忽略)
Vite 中 critters 插件没生效
根本原因是 Vite 默认不预渲染 HTML,critters 没有真实 DOM 快照可分析。它不是靠正则扫 CSS 文件,而是模拟浏览器加载首屏 HTML 后计算样式覆盖率。
实操建议:
- 开启
build.ssr: true或用vite-plugin-prerender提前生成静态 HTML 快照;否则critters只看到空<body></body> - 推荐直接用
vite-plugin-critters(非官方但维护活跃),它会自动接管 HTML 构建流程,比手动加critters()插件更稳 - 如果首屏依赖异步路由组件(如
router.push),得在 prerender 阶段显式配置prerenderRoutes: ['/'],否则 critters 不知道该抓哪块 DOM
Less 项目里怎么避免 critical.css 体积虚高
默认 lessc 编译会全量打包所有 @import 的样式(含非首屏、动画、:hover 等),导致内联后体积膨胀、CSSOM 构建延迟。
实操建议:
- 在变量和混入文件开头加
@import (reference),只导入定义,不输出样式代码 - 新建独立入口
critical.less,只@import (reference)公共变量/混入,再手动写或调用真正需要的规则 - Webpack/Vite 构建时,用
mini-css-extract-plugin单独处理这个入口;Vite 用户优先选vite-plugin-critical-css,它基于真实 SSR HTML 提取,比静态分析准 - 用 Chrome DevTools → Coverage 验证:禁用缓存刷新,确认
critical.css中绿色(已使用)字节占比 ≥95%
@import (reference) 的写法、pruneSource 的开关、include 的容器选择器,这些细节直接决定提取结果是否干净。工具不会替你判断 .hero[open] 是否首屏可见,它只认 DOM 快照里的 class 名字。


















