做前端这些年来,CSS 是唯一一个让我又爱又恨的技术。爱的是它足够灵活,恨的也是这份灵活——全局作用域、层叠优先级、选择器权重,这些特性在个人页面里是玩具般的存在,一旦进入大型项目,就成了级联混乱的根源。我参与过的团队里,几乎每周都会有人因为改一个 .title 的样式而影响其他页面的布局。正因为如此,CSS 的工程化方案才会从小圈子里的探究,逐步演变成每个前端团队都绕不开的必修课。
今天想跟你聊的,是当前前端社区讨论最集中的“CSS 三大主流方案”:以 BEM + 预处理器为代表的传统 CSS 方案、编译期完成隔离的 CSS Modules、以及完全由 JavaScript 驱动的 CSS-in-JS。这三种方案本质上都指向同一个问题——如何让全局性的 CSS 在组件化的世界里安全、高效地运行。不管你是刚接触组件化开发的新手,还是正在做技术选型的团队负责人,这篇文章都会结合我多年的实际项目经验,把每条路线的原理、代码形态、坑点以及选型依据一次讲透。
1. 为什么 CSS 需要“方案”,它到底在解决什么
在页面开发还没有组件化的年代,CSS 的全局特性并不是问题。一个 HTML 文件配一个或几个 CSS 文件,只要命名统一,很少出乱子。但组件化开发普及之后,场景彻底变了:同一个页面由几十上百个组件拼接,每个组件都有自己的样式文件,而这些文件里的类名仍然共享同一个全局命名空间。这意味着,你在任何组件里写下一个 .button,它都有可能误伤页面里其他组件的同名类。
1.1 全局命名空间才是万恶之源
CSS 中的每个选择器默认都是全局的,这一点和 JavaScript 的模块化完全不同。JS 里你 import 一个模块,它的内部变量和函数不会跑出去污染别的模块;CSS 没有这样的隔离机制。举个实际例子:A 组件里定义 .card { padding: 16px; },B 组件里也定义一个 .card { padding: 8px; },两个类名在同一个文档里同时存在时,谁的规则生效取决于加载顺序和选择器权重,而不是组件的嵌套深度。结果就是,页面样式在不知不觉中被改掉,排错时你根本想不到问题居然出在隔壁组件。
1.2 层叠模型里的“暗雷”不止一个
除了全局性,CSS 还牵扯到层叠和继承。一个元素的最终样式,是选择器优先
