vite ssr中css未注入是因缺少vite-plugin-css-injected-by-js插件;关键css需用critters精准提取并内联,避免fouc与ttfb延迟。

SSR中CSS没进?不是漏了,是Vite没配对插件
你在Node.js SSR里import './index.css',但生成的HTML里空空如也——这不是代码写错了,是Vite默认不收集CSS字符串。它只触发模块加载,不把样式内容吐给服务端。必须加vite-plugin-css-injected-by-js,它能让服务端运行时把已加载的CSS拼成字符串,再塞进renderToString返回的context里。
注意:别用<link rel="stylesheet">替代,那是客户端异步加载,必然FOUC。React/Vue同构应用里,样式收集漏掉一层(比如<router></router>或<suspense></suspense>边界)就白干。
- React +
styled-components:必须用ServerStyleSheet,且collectStyles(<app></app>)要包裹最外层,包括<router></router> - Vue 2:
renderer.renderStyles(context)必须在renderToString之后调 - Vue 3:必须传
{ styleSheets: true },否则context.styles为空 - 用了
defineAsyncComponent或lazy()?动态组件里的import './xxx.module.css'不会被自动收集,得手动预加载或触发
关键CSS不能全量内联,体积超14KB会拖慢TTFB
把整个main.css塞进<style></style>看似省事,实则反效果:首屏只用其中20%的规则,却让index.html变大100KB,弱网下多等300ms才开始解析。关键CSS是“首屏渲染必需的那部分”,不是全量压缩,而是精准裁剪。
工具链里critters比Penthouse更轻量、更适配Vite,它直接操作HTML AST,在构建阶段把关键样式内联进,同时把剩余CSS转成<link rel="preload" as="style">。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 安装:
npm install critters --save-dev - Vite配置:
build.rollupOptions.plugins.push(critters()) - 它默认只处理
<link rel="stylesheet">,不处理<style></style>或style属性;动态插入的样式表得手动加data-critters="skip"避免误处理 - 输出结构类似:
<style data-critters>h1{font-size:24px}</style> <link rel="preload" as="style" href="/assets/style.abc123.css" onload="this.onload=null;this.rel='stylesheet'">
extractCritical返回空?大概率是HTML没带真实样式
extractCritical(来自critters或旧版penthouse)返回空css字段,90%不是代码问题,而是输入HTML没含真实样式资源:
- 默认不下载远程
<link rel="stylesheet">,若路径404或缺<base>,就解析不出规则 - SSR渲染后的HTML若含动态class(如
class="btn btn--primary is-loading"),而工具只看到模板里的btn,就会漏掉is-loading对应样式 -
@import未提前用postcss-import展开,critters直接跳过整条规则
实操建议:
- 用
fs.readFileSync把CSS文件读成字符串,显式传给extractCritical的css参数,绕过路径解析 - 确保传入的HTML字符串已内联全部基础样式,或至少是本地可访问的完整快照
- 静态提取时加
pruneSource: true+include: '#app',限制分析范围,避免扫进node_modules里未用的样式
内联后验证是否真起效?别只看<style data-critters></style>
HTML里有<style data-critters></style>不代表关键CSS真起效。重点看实际渲染行为:
- Chrome DevTools → Network → Disable cache,刷新页面,观察Waterfall中
index.html下方是否立刻出现render-blocking样式(没有则说明关键CSS已覆盖) - 检查是否仍有FOUC:内联块生效了,但原来的
<link rel="stylesheet" href="style.css">还在DOM里,浏览器会先画一次内联样式,等外部CSS加载完再重绘一遍 - 内联块里禁止出现:
@import、@font-face、任何含url()的声明——这些该留在非关键CSS中,用rel="preload"控制加载时机 - 必须从HTML中彻底移除所有被提取覆盖的
<link>标签,不能只靠内联就以为万事大吉
复杂点在于:伪类(.btn:hover)、继承链中的上级规则(article p)、字体fallback声明,这些自动化工具容易漏判,得人工校验。内联本身不支持缓存复用,每次HTML更新都要重新传输,所以必须配合构建流程自动化生成,并确保与主样式表同步。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










