📏 CSS 方法论(OOCSS / BEM / SMACSS / ITCSS)

一句话定性

CSS 的选择器天生是全局的——这在一篇文档里无害,在一个百人团队的大型应用里却是原罪。在没有任何技术手段隔离样式的年代,前端只能靠纯粹的人类约定来对抗混乱:用命名规范、分层架构、组合思想来”假装”CSS 有作用域和模块化。CSS 方法论,就是工具到位之前,人类用纪律换秩序的一段历史。


一、它们是什么 & 出现的时代

CSS 方法论是一组关于”如何组织和命名 CSS”的约定与最佳实践,流行于 2005-2013 Ajax时代后期到 2013-2018 SPA时代早期(约 2009–2016)。它们不是工具、不是库、不需要编译,纯粹是写法上的纪律。代表四套:

方法论提出者 / 年份核心思想一句话
OOCSSNicole Sullivan,2009面向对象 CSS:结构与皮肤分离、容器与内容分离把样式当”可复用对象”
BEMYandex(俄罗斯)Block__Element—Modifier 命名用长类名”伪造”作用域
SMACSSJonathan Snook,2011按角色把规则分成 5 类(Base/Layout/Module/State/Theme)给 CSS 分层归类
ITCSSHarry Roberts按 specificity 从低到高分层倒三角组织驯服优先级与级联

二、为什么会出现:CSS 全局作用域引发的三场战争

大型项目里,全局 CSS 必然失控的三个机制

CSS 没有作用域、没有模块、没有命名空间。当代码量和团队规模增长,三种灾难必然发生:

  1. 命名冲突:你写 .button,同事也写 .button,后加载的覆盖先加载的。改 A 页面的样式,B 页面莫名其妙崩了。没人敢删任何一行 CSS,因为不知道哪里还在用它(append-only CSS:只敢加不敢改)。
  2. specificity 战争:为了让自己的样式”赢过”别人,大家比赛写更长更具体的选择器(.header .nav ul li a.active)。优先级越堆越高,最后没人能覆盖谁
  3. !important 核武器:当 specificity 也卷不动了,就祭出 !important。一旦有人用了,别人只能用更多的 !important 还击。这是 CSS 工程失控的最终症状——核平等于谁都别想活

这三场战争的根因是同一个:CSS 的全局命名空间,在缺乏隔离手段时,把”局部决策”变成了”全局后果”。 方法论的全部努力,都是在用约定人为制造隔离


三、核心机制 & 为什么各自流行

OOCSS(2009,Nicole Sullivan)—— 思想奠基者

Sullivan 把软件工程的面向对象思想引入 CSS,提出两条原则:

  • 结构与皮肤分离:布局结构(尺寸、定位)和视觉皮肤(颜色、圆角)拆成独立的类,自由组合。
  • 容器与内容分离:.title 的样式不该依赖它在 .sidebar 里还是 .main 里——样式应可移植。

OOCSS 的历史地位

它本身没有严格的命名规范,但它第一次把”复用”和”组合”作为 CSS 的第一性原则提出来。这个”用小的、可组合的类拼出界面”的思想,直接是日后原子化 CSS的精神祖先。

BEM(Yandex)—— 用命名”伪造”作用域

BEM 是流传最广、最实用的一套。它规定类名必须是 block__element--modifier 形式:

.card { }              /* Block:独立组件 */
.card__title { }       /* Element:组件的一部分 */
.card__title--large { }/* Modifier:变体 */

BEM 为什么赢了:它最直接地攻击了"作用域"痛点

类名 .card__title 长到几乎不可能和别人撞车,等于用命名约定手工模拟了命名空间。而且它强制扁平的单层选择器(永远只用一个 class,不嵌套),从源头杜绝了 specificity 战争——所有规则优先级都一样,谁后写谁生效,简单可预测。BEM 的成功证明:当时大家最痛的不是复用,而是冲突。

SMACSS(2011)与 ITCSS —— 治理”级联”本身

如果说 BEM 管”单个名字”,SMACSS 和 ITCSS 管的是”整个代码库怎么分层”:

  • SMACSS 把所有规则按角色分成 Base / Layout / Module / State / Theme 五类,各司其职。
  • ITCSS 更进一步,按 specificity 从低到高排成一个”倒三角”层级(从通用的 reset 到具体的覆盖),从架构上保证”低优先级的在前、高优先级的在后”,让级联可控、可预测

它们流行,是因为在 SPA 之前,CSS 缺乏任何架构层面的组织方式,大团队迫切需要一套”放东西的规矩”。


四、带来的新问题 / 副作用

约定的根本脆弱性:它靠人自觉

方法论本质是社会契约,而非技术强制。它的所有问题都源于这一点:

  1. 没有强制力:编译器不会因为你不写 BEM 就报错。团队里只要有一个人偷懒、有一个新人不懂规范,体系就开始腐烂。约定的执行成本完全压在 code review 和团队自律上。
  2. 类名臃肿、心智负担重:block__element--modifier__sub--state 这种长名字写起来啰嗦,记起来累。HTML 里 class="card card--featured card__body card__body--large" 也不好看。
  3. 无法删除死代码:即便用了方法论,你依然无法确定一条 CSS 还有没有被用到——全局作用域的根本问题没有被消除,只是被”绕过”了。
  4. 复用与冲突的矛盾没真正解决:BEM 用”不复用名字”换”不冲突”,代价是几乎放弃了跨组件的样式复用。

五、为什么会衰落 / 现状:从”约定”到”工具”的范式转移

方法论的死穴:它在解决一个本该由工具解决的问题

用第一性原理看:作用域冲突的本质,是缺少”自动生成唯一标识”的机制。 而这件事,人脑用命名约定来做,既费力又不可靠;交给编译器,却轻而易举。一旦构建工具(Webpack/Vite)普及,样式可以走编译流程,“用工具消灭冲突”就碾压了”用纪律避免冲突”。

方法论被三波工具化方案逐步取代:

CSS 全局作用域(命名冲突 + specificity 战争 + !important)
        │
        ├──【人治阶段】用约定对抗(2009–2016)
        │     OOCSS(组合思想)→ BEM(命名伪作用域)
        │     → SMACSS / ITCSS(分层治理级联)
        │     痛点:靠人自觉,规模一大就失守
        │
        ▼
        【法治阶段】用工具消灭(2015 起)
        ├─ CSS Modules:编译期把类名 hash 成唯一值
        │        → "真·局部作用域",根本不用再手写 BEM        → [[CSS-in-JS]]
        │
        ├─ CSS-in-JS:样式直接写进组件,作用域天然隔离        → [[CSS-in-JS]]
        │
        └─ 原子化 CSS:根本不命名,直接用预定义 utility 类     → [[原子化CSS]]
                 (OOCSS"组合"思想的工业化终点)

方法论留下了什么(它没有白死)

  1. OOCSS 的”组合优于继承”思想,在 原子化里以工业化形式复活——utility class 就是把”小而可组合的对象”做到极致。
  2. BEM 的”局部作用域意图”,被 CSS Modules 用编译期 hash 自动实现了——人想做的事,机器替你做了。
  3. ITCSS 对级联的治理思想,最终被 CSS 原生的 @layer(级联层) 标准化收编——又一次”社区先发明,标准后追认”。

方法论是一段”工具缺位时,人类用纪律硬扛”的过渡史。它没有失败,只是完成了历史使命:它精确地定义了问题,后来的工具才能精准地解决问题。


🔗 同组:CSS演进史 | 布局演进史 | CSS预处理器 | CSS-in-JS | 原子化CSS 🔗 相关:2005-2013 Ajax时代 | 2013-2018 SPA时代 | Webpack | jQuery