critical css 提取需基于真实首屏 dom、移动端视口并剥离非首屏样式;extractcritical 返回空常因未加载远程 css、路径错误或动态 class 未被识别;内联后须移除对应 link 标签且禁用 url()、@font-face 等同步请求声明。

提取 Critical CSS 并内联,不是加个插件就能自动生效;它必须基于真实首屏 DOM 结构、移动端视口、且剥离所有非首屏样式,否则反而拖慢 TTFB 或引发 FOUC。
extractCritical 返回空字符串怎么办
这不是代码写错了,是工具根本没看到有效样式规则。常见原因包括:
-
extractCritical(来自critters或旧版penthouse)默认不加载远程<link rel="stylesheet">,路径 404、缺<base>、或 CSS 文件还没生成到dist/目录,就解析不出任何内容 - SSR 渲染出的 HTML 含动态 class(如
class="btn btn--primary is-loading"),但提取器只扫描模板字符串里的btn,漏掉is-loading对应的规则 -
@import没被postcss-import展开,整条规则直接跳过
实操建议:
- 用
fs.readFileSync('./dist/style.css', 'utf8')显式传给extractCritical的css参数,绕过路径解析 - 确保传入的 HTML 是 SSR 完整快照(比如从
dist/index.html读取),而非原始模板字符串 - 加
pruneSource: true和include: '#app',限制分析范围,避免扫进node_modules里未用的样式
内联后页面反而闪动(FOUC)更严重
这不是 Critical CSS 本身的问题,是加载链冲突了:
- 你把样式内联了,但原来的
<link rel="stylesheet" href="style.css">还在 DOM 里,浏览器先画一次内联样式,等外部 CSS 加载完又重绘一遍 - 更糟的是,如果内联块里混进了
@font-face或url(/img/hero.jpg),这些会同步触发网络请求,卡死 CSSOM 构建
实操建议:
- 必须从 HTML 中彻底移除所有被提取覆盖的
<link>标签,不能只靠内联就以为万事大吉 - 内联块里禁止出现:
@import、@font-face、任何含url()的声明——这些该留在非关键 CSS 中,用rel="preload"控制加载时机 - 非关键 CSS 推荐用
rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'",别用media="print"hack(兼容性差、执行不可控)
构建时提取的 Critical CSS 里全是 .modal-hidden、.user-dropdown--closed
这是静态提取最典型的误判:构建时 HTML 模板还没被组件真正渲染,插件只能看到原始字符串里的 class,没法知道哪些会在 hydration 前真实挂载。
实操建议:
- SSR 场景(如 Next.js、Nuxt)优先在
getStaticProps或generateStaticParams里用renderToStaticMarkup预生成真实首屏 HTML 片段,再喂给critters - 若必须构建时提取,限定范围:
include: 'body > #root'+pruneSource: true,并拆分样式:首屏专用critical.css单独import,其余走 lazy CSS - 禁用条件渲染的 loading skeleton——比如不要在 SSR 阶段用
{isLoading ? <skeleton></skeleton> : <content></content>},否则提取器看到的只是<skeleton></skeleton>的 DOM
最关键的细节往往藏在「移除已覆盖的 <link>」和「内联块中禁用 url()」这两步里——很多人做完提取和内联就以为结束了,结果 TTFB 变长、FOUC 更明显、字体加载被阻塞,问题全出在这两个被忽略的清理动作上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











