CSS-in-JS 在中后台项目中并非不能用,但在缺少规范时很容易失控。核心原因通常集中在组件粒度混乱、主题样式扩散缺乏约束,以及 SSR 场景下类名不一致。更稳妥的做法是:将 css 函数限定在业务组件中使用,基础组件统一采用 CSS Modules,主题样式交由 CSS 变量管理,同时在 SSR 中统一 cache 并提前提取样式。

CSS-in-JS 在中后台项目中并不是“天然不适合”,但一旦缺乏明确边界和工程约束,就非常容易迅速失控——问题的关键不在语法本身,而在组件粒度失衡、主题扩散失控,以及 SSR 一致性不足这三个核心环节。
中后台项目中 CSS-in-JS 失控的根本原因,是组件复用与样式耦合过深
中后台系统通常会大量依赖可配置、可复用的通用组件,例如 Table、Form.Item、Card 等。这类组件往往会在不同业务模块、不同子包之间被频繁复用。一旦每个使用方都通过 styled.xxx 或 css 函数重新包装和改写样式,就很容易引发以下问题:
- 同一个逻辑组件在不同页面或不同模块中生成不同的 hash 类名(例如
sc-abc123和sc-def456),导致无法基于 class 名进行统一覆盖、排查和调试 - 样式逻辑分散在多个位置:主题色通过 props 传递、尺寸从 context 获取、禁用态逻辑重复实现多次,最终缺少统一的样式管理入口
- 构建产物中相同的样式规则会被重复序列化并注入,
标签数量随着路由和页面增加而线性膨胀,难以有效收敛
在主题切换场景下,CSS-in-JS 很容易演变为状态管理难题
中后台项目几乎都会涉及暗黑模式、多租户主题、品牌换肤、A/B 测试等需求。但很多 CSS-in-JS 方案默认将主题值作为 props 直接参与样式计算,这会进一步带来一系列连锁问题:
- 每次主题切换时,所有包含插值计算的组件都可能触发重新渲染(即使界面本身没有实际变化),因为
props.theme往往是新的对象引用 - 采用
css({ color: theme.primary })这类写法时,如果theme对象没有经过 memo 化处理,就会导致哈希缓存失效,重复生成并插入样式 - 在服务端渲染场景中,如果主题 token 来源于请求上下文(如 cookie 或 header),而客户端 hydration 阶段读取的是 localStorage,默认值不一致时就会直接引发类名 mismatch 问题
在 SSR + CSR 混合渲染场景中,hash 不稳定往往是最隐蔽的风险点
很多中后台项目都会启用 SSR(如 Next.js / Nuxt / Rspack SSR),而 CSS-in-JS 的类名稳定性又高度依赖构建配置和运行时环境。如果处理不当,就会出现静默且难排查的问题:
- 未启用
@emotion/babel-plugin或babel-plugin-styled-components时,服务端生成的类名可能基于 AST,而客户端生成逻辑却依赖运行时字符串,最终几乎必然出现 mismatch - 多个子包分别安装不同版本的
@emotion/cache时,会各自维护独立的 cache 实例,导致同一段样式被重复插入,并且插入顺序难以预测和控制 - 在动态 import 组件中使用
styled时,由于 chunk 加载时机不稳定,标签插入顺序容易错乱,进而造成样式优先级覆盖异常(例如Button的 hover 样式无法覆盖Modal的 backdrop)
真正可行的优化方案,并不是简单禁用 CSS-in-JS,而是把它严格限制在“有限动态样式”的使用范围内:只允许在业务组件中使用 css 函数封装原子化样式,基础组件统一使用 CSS Modules;主题变量必须通过 CSS 自定义属性(var(--color-primary))向下透出,JavaScript 层只负责开关切换,不直接参与主题计算;同时,所有 SSR 入口都必须强制共享统一的 cache 实例,并在服务端提前提取样式,才能从根本上降低 CSS-in-JS 在中后台项目中的失控风险。
