BEM与SMACSS需结合使用:SMACSS五类结构框定BEM边界,仅Module层用BEM命名,Base/Layout层禁用,State/Theme类须与BEM分离;二者协同控制CSS权重与文件组织。

单独用 BEM 容易陷入“只管命名、不管分层”的陷阱,单独用 SMACSS 又可能类名语义模糊、组件边界不清。两者结合不是简单叠加,而是用 SMACSS 的分类骨架约束 BEM 的落地范围,再用 BEM 的命名精度填充 SMACSS 各层的具体实现。
SMACSS 的五类结构如何框定 BEM 的使用边界
SMACSS 把样式按职责划为 Base、Layout、Module、State、Theme 五类,而 BEM 主要服务于 Module 层——即真正可复用、有交互、需独立维护的 UI 组件。这意味着:
-
Base层不写 BEM:比如h1、button的重置样式,直接用标签选择器或原子类(如.text-sm),BEM 在这里冗余且破坏默认行为 -
Layout类只用 SMACSS 命名(如.l-header),不套 BEM:布局容器本身不封装逻辑,强行加header__logo会把布局细节泄漏到模块层 -
Module层是 BEM 主战场:每个.card、.search-form都严格按 Block/Element/Modifier 拆解,确保组件内聚 -
State类(如.is-open)和Theme类(如.theme-dark)必须与 BEM 类共存但分离:不能写成.card--is-open,而应是.card.is-open,否则破坏 BEM 修饰符只表达“静态变体”的本意
BEM 的命名刚性 + SMACSS 的分类弹性 = 更可控的 CSS 权重
纯 BEM 写多了容易堆出超长选择器(比如 .sidebar__nav__item__link--active),而 SMACSS 要求模块选择器深度 ≤2 层,这就倒逼你在 Sass 中做取舍:
- 用
&__item嵌套没问题,但禁止&__item &__link这种二级嵌套——后者编译出来是.sidebar__nav__item .sidebar__nav__item__link,权重翻倍且违反扁平原则 - 当一个模块内部真有深层结构(比如树形菜单),应拆成多个 Block:
.menu+.menu-item+.menu-sub,而不是硬塞进一个 Block 的嵌套里 -
State类必须通过 JS 切换,且在 CSS 中用低权重声明:.card.is-collapsed .card__content { max-height: 0; },而非.card--collapsed__content——后者会让状态逻辑和样式耦合,难以被其他模块复用
为什么项目越大会越需要这种组合
小项目靠命名自觉就能管住样式;中大型项目里,光靠 BEM 命名无法回答“这个类该放在哪个文件”“它属于基础样式还是业务模块”。SMACSS 的目录结构(如 base/_reset.scss、layout/_header.scss、modules/_card.scss)配合 BEM 文件内组织,才能让新成员快速定位:
立即学习“前端免费学习笔记(深入)”;
- 看到
.btn--primary,立刻知道它属于modules/_button.scss,且只影响按钮自身变体 - 看到
.l-grid,就知道去layout/下找,不会误改成组件逻辑 - 遇到
.is-loading,直接查state/目录,不用翻遍所有组件文件找加载态实现
真正容易被忽略的是:BEM 和 SMACSS 都不解决“什么时候该抽新 Block”这个问题。比如一个带搜索、筛选、排序的表格,是做成 .table 一个 Block,还是拆成 .table + .table-toolbar + .table-sorter?这得靠团队对“独立可复用”边界的共识,而不是规范本身能定义的。


















