ssr中css需内联至html的中以避免闪屏,推荐用critters动态提取关键css;服务端构建应禁用minicssextractplugin,改用css-loader/locals或手动注入转义后的css字符串。

Node.js服务端渲染时,CSS文件怎么加载才不闪屏
服务端渲染(SSR)中直接用 <link rel="stylesheet"> 会导致首屏样式延迟应用:HTML先出来,CSS后加载,页面短暂无样式或错位。关键不是“加不加”,而是“什么时候、以什么方式把CSS内容塞进HTML里”。
- 静态
<link>标签只适合纯客户端渲染,SSR必须把关键CSS内联到中 - 不能靠浏览器去请求CSS文件——那和CSR没区别,SSR的首屏优势就没了
- 如果项目用 Webpack,
MiniCssExtractPlugin默认只在生产环境提取CSS文件,开发时它不生效,容易误以为“本地跑得通,上线就白屏”
用 critters 提取并内联关键CSS(Critical CSS)
硬塞全部CSS进HTML会增大HTML体积,影响TTFB;只塞首屏需要的才是合理做法。critters 是目前最轻量、适配现代SSR框架(如Express + React/Vue)的方案,它能解析HTML字符串,自动提取关键CSS并内联。
- 它不依赖构建时预分析,而是在每次响应时动态处理生成的HTML字符串,适合服务端每次渲染都可能不同的场景
- 注意:必须确保传入的HTML是完整、已闭合的文档(含
、),否则critters会跳过或报错Error: No element found - 默认只处理
<link rel="stylesheet">,对@import或内联<style></style>不处理,别混用 - 示例用法:
const Critters = require('critters');<br>const critters = new Critters({ inlineFonts: false });<br>const htmlWithInlinedCSS = await critters.process(htmlString);
Webpack打包中避免CSS被拆成独立文件(SSR构建阶段)
如果你用 Webpack 打包服务端代码(比如 target: 'node'),但又在入口里写了 import './index.css',Webpack 默认仍会尝试用 MiniCssExtractPlugin 提取——这在Node环境根本跑不起来,会报 ReferenceError: document is not defined。
- 服务端打包配置里必须禁用所有浏览器端CSS处理插件:删掉或条件排除
MiniCssExtractPlugin.loader,改用css-loader/locals或直接忽略CSS(靠客户端再加载) - 更稳妥的做法是:服务端只负责收集CSS路径或内容,不执行CSS模块;真正解析和注入交给
critters或手动读取CSS文件字符串 - 如果用了
css-modules,服务端无法运行require('xxx.module.css'),应通过css-loader的exportOnlyLocals: true配合构建时JSON导出,而非运行时导入
Vue SSR / React SSR 中手动注入CSS字符串的兜底写法
当工具链不统一、或需要精细控制(比如按路由分块注入),就得自己拼HTML。核心就两步:读CSS文件内容 → 插入到 的合适位置。
- 别用
fs.readFileSync同步读取,Node.js服务端高并发下容易阻塞事件循环;用fs.promises.readFile+await - CSS内容必须转义,防止
<style>...</style>里的特殊字符破坏HTML结构,用html-escaper或简单正则.replace(/&/g, '&').replace(/, ' - 插入位置很重要:放在
<title></title>后、其他<script></script>前,否则可能触发浏览器重排或阻塞JS执行 - 示例:
const css = await fs.promises.readFile('./dist/main.css', 'utf8');<br>html = html.replace('', `<style>${css}</style>`);
<link> 的请求——除非你手动移除它。否则既浪费请求,又可能导致样式重复计算。这个动作必须在 critters.process() 后、返回HTML前显式做掉。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











