CSS 模块化架构的演进:BEM、CSS Modules 到 CSS-in-JS 的反思

CSS 模块化架构的演进:BEM、CSS Modules 到 CSS-in-JS 的反思

一、引子:那个 BEM 类名比内容还长的组件

翻到五年前写的代码,一个按钮的 class 是 header__nav--primary-btn__label--is-active。它在 2019 年是正确的------BEM 方法论是当时应对"CSS 全局作用域"问题的最佳实践。但站在 2026 年回头看,这条演进路径上有太多"当时看起来正确,后来发现代价很大"的决策。

CSS 模块化的演进是一场"作用域控制 vs 表达能力 vs 性能"的三角博弈。每一代方案都在优化其中两个维度,代价是牺牲第三个。

二、先把评价标准说清楚

选样式方案时,别只比较写一段按钮样式要几行代码。至少要看四件事:样式能否被安全删除、主题变量是否能统一管理、服务端渲染是否稳定,以及新人能否在不阅读整套约定的情况下修改一个组件。团队若主要维护长期产品,最后两项通常比"首次开发速度"更重要。

也不要把 BEM、CSS Modules 和 CSS-in-JS 当作互斥阵营。一个项目可以用 CSS Modules 处理业务组件,用少量全局 CSS 放 Token 和 reset;已有的 CSS-in-JS 组件也不必为了风格统一而一次性重写。先约束新增代码,再在改动旧页面时顺手迁移,成本更可控。

三、各方案的适用场景与问题

BEM:适合项目初期,维护成本随规模增长

BEM 的核心思想是"用命名约定模拟作用域"。block__element--modifier 的命名模式提供了人工可读的作用域信息。问题是它依赖开发者自律------哪天有人写了 .header .btn 而不是 .header__btn,没有编译器会报错。

适用:简单网站、不需要组件化的小项目。

CSS Modules:2026 年仍然是最佳平衡点

CSS Modules 在编译时对类名进行哈希处理,确保每个组件的样式被隔离到唯一的类名空间。它保留了完整的 CSS 表达能力(不像 CSS-in-JS 限制某些选择器),且没有运行时性能开销(所有处理在构建时完成)。

css 复制代码
/* Button.module.css */
.button {
  /* 编译后: .Button_button_abc123 */
  composes: base from './base.module.css';
  padding: var(--spacing-md);
}

适用 :绝大多数 Web 应用。配合 CSS 自定义属性(Token)和 @layer 管理优先级。

CSS-in-JS:运行时成本被低估了

Styled Components 和 Emotion 在 2020-2024 年间非常流行。它们的优势是"样式和逻辑在一起"的 colocation 理念和动态样式的便利性。

但运行时成本是一个严重问题:

  • 每个样式对象需要序列化 → 注入 <style> 标签
  • SSR 时样式需要额外处理(提取关键 CSS)
  • 包体积增加 12-15KB(gzip)
  • 在 React 18 并发模式下有兼容性问题

2025 年开始,社区明显在回归编译时方案(零运行时 CSS-in-JS 如 Panda CSS、Vanilla Extract,或回归 CSS Modules)。

四、2026 年的推荐架构

复制代码
CSS 架构 = CSS Modules + CSS Custom Properties + @layer

- CSS Modules:组件级作用域隔离
- CSS Custom Properties:设计 Token + 主题切换
- @layer:优先级管理(reset → base → components → utilities)

五、迁移建议

从全局 CSS 或 BEM 迁移时,先找样式冲突最多、改动最频繁的页面作为试点。把公共颜色和间距提取为变量,再迁移组件本身;不要先批量改类名。每迁移一个页面,用视觉回归截图比对桌面和移动端,并检查键盘焦点、禁用态和高对比度主题。这样能把"样式重构"变成可回滚的小批次,而不是一次难以验证的改造。

六、结论

  1. CSS 模块化经历了 BEM(命名约定)→ CSS Modules(编译时)→ CSS-in-JS(运行时)→ 回归编译时四个阶段
  2. 2026 年最佳平衡点是 CSS Modules + CSS 自定义属性 + @layer
  3. CSS-in-JS 的运行时成本(SSR 性能、包体积)是回归编译时方案的主因
  4. BEM 在简单项目中仍然可用,但缺乏编译器级别的安全保障
  5. 零运行时 CSS-in-JS(Panda CSS, Vanilla Extract)是兼顾 colocation 和性能的折中方案
  6. 选择方案时,优先看维护方式和渲染路径,不要只看语法偏好
相关推荐
IT_陈寒1 小时前
Vue的computed属性把我坑惨了,原来我一直用错姿势
前端·人工智能·后端
小灰灰搞电子2 小时前
Rust+Slint 实现温度计源码分享
前端·rust·slint
计算机魔术师2 小时前
面壁智能 OpenBMB 推出 MathForm,面向 Lean 4 数学自动形式化的开源框架、数据集与模型
前端
NeilCarmack2 小时前
Deepseek-harness增加桌面版端序列:第 2 讲 · spawn Electron:当前进程如何“交棒“
前端·javascript·electron
上海魁鲸科技有限公司3 小时前
APS高级排产系统到底有什么用?一文讲清功能、选型与落地建议
前端·microsoft·excel
陈随易3 小时前
Bun v1.4 更新总结:把浏览器、图片、定时任务和工程工具都装进一个运行时
前端·后端·程序员
东风破_5 小时前
TypeScript 高级类型进阶:keyof、Exclude、Record 与类型组合思想
前端·后端·typescript
DS随心转插件5 小时前
Grok生成的html怎么导出——AI导出鸭:大模型结构化输出的“最后一公里”工程化解构
前端·人工智能·ai·html·豆包·deepseek·ai导出鸭
এ慕ོ冬℘゜5 小时前
使用 jQuery 动态渲染表格与状态切换
前端·javascript·jquery