Less 本身不参与 SSR 样式提取,所谓“SSR 闪烁”实为编译产物未正确注入或加载顺序错乱导致的 FOUC;核心问题在构建与注入环节,而非 Less 本身。

Less 本身不参与 SSR 样式提取——它只是编译时预处理器,服务端渲染时早已被转成普通 CSS。所谓“Less 的 SSR 闪烁”,实际是 less 编译产物未被正确注入或加载顺序错乱导致的 FOUC(Flash of Unstyled Content)。核心问题不在 Less,而在构建和注入环节。
为什么 extract-css-chunks-webpack-plugin 在 SSR 中失效
该插件依赖 Webpack 运行时的 __webpack_require__.miniCssFulfil 等钩子,在 Node.js 环境下无法触发 CSS 提取逻辑。服务端渲染时,Webpack 的 style-loader 会退化为内联 style 标签(甚至直接丢弃),而客户端又试图重新挂载同名 CSS 文件,造成样式不一致。
- 服务端:CSS 被忽略或仅以字符串形式存在,未注入 HTML
- 客户端:CSS 文件通过
<link rel="stylesheet">加载,但此时 DOM 已渲染完毕,样式滞后 - 结果:首屏先显示无样式的 HTML,再闪一下才应用样式
如何用 less-loader + MiniCssExtractPlugin 正确分离 SSR CSS
关键不是“提取”,而是“确保服务端能拿到编译后 CSS 字符串并内联到 HTML 中”。MiniCssExtractPlugin 只在生产构建时生效,且默认只输出文件,不提供运行时字符串访问接口。
- 必须禁用服务端构建中的
MiniCssExtractPlugin.loader,改用css-loader+less-loader链,保证 CSS 被解析为 JS 模块导出 - 在服务端入口中,显式
import所有主样式文件(如./index.less),让 Webpack 将其作为模块处理 - 使用
css-loader的exportType: 'css-style-sheet'(Webpack 5.30+)或自定义 loader 获取 CSS 文本 - 避免
style-loader:它在 Node 环境下无副作用,等于没加载
服务端注入 CSS 字符串的最小可行方案
不要依赖插件自动注入;手动收集、拼接、写入 HTML 的 <style></style> 标签才是可控路径。
立即学习“前端免费学习笔记(深入)”;
- 在服务端渲染前,执行一次
require('./index.less')(需配置less-loader+css-loader?modules=false) - 利用
css-loader默认导出的locals不适用,改用exportType: 'string'得到纯 CSS 文本 - 将所有样式文本合并后,插入到
renderToString()返回的 HTML 字符串中,位置在内、<script></script>标签之前 - 客户端不再加载同名 CSS 文件(移除
<link>),避免重复或冲突
const cssText = require('!!css-loader?exportType=string!less-loader!./index.less');
Next.js / Remix 等框架中容易被忽略的陷阱
框架的 getServerSideProps 或 loader 函数运行在纯 Node 环境,没有 DOM、没有 window、也没有 document.createElement。任何依赖运行时样式注入的方案(比如靠 useEffect 动态 append style)在服务端根本不会执行。
-
less.modifyVars是浏览器端 API,服务端调用会报ReferenceError: window is not defined - 主题变量替换必须在构建时完成(如通过
less-loader的modifyVars选项),不能留到服务端运行时 - 如果用了 CSS Modules,服务端无法识别类名哈希,会导致 class 名不匹配、样式丢失
真正起作用的永远是那一段被提前编译好、无条件注入的 CSS 字符串——其余都是障眼法。


















