block__el1__el2是非法bem命名,因其违反“element必须直属block”的核心定义;它暗示el2属于el1而非block,破坏语义归属与组件边界,导致复用崩塌、js定位错位及选择器耦合。

为什么 block__el1__el2 是非法命名
BEM 不是“不推荐”这种写法,而是直接判定为语法错误——block__el1__el2 违反了 BEM 对 Element 的根本定义:它必须是 Block 的直属子节点,不能是另一个 Element 的子节点。
这背后不是格式洁癖,而是结构契约:card__header__title 暗示 DOM 中存在 <div class="card__header"><div class="card__header__title"> 这样的嵌套,但此时 <code>card__header__title 实际上已脱离 card 直接挂载在 card__header 下,语义上它就不再是 card 的元素,而是 card__header 的子组件。BEM 要求每个组件边界清晰,不允许模糊归属。
block__el1__el2 会引发哪些实际问题
这类命名看似省事,但在工程中会快速暴露耦合风险:
- 选择器权重失控:
.card__header .card__header__title(如果用后代写法)比.card__header-title多一层依赖,改card__header的display可能意外破坏card__header__title的布局逻辑 - JS 操作失焦:用
querySelector('.card__header__title')时,你实际想定位的是“属于 card 的标题”,但 DOM 结构已把它划归到header下,语义和实现错位 - 复用性崩塌:一旦
card__header__title被抽到用户列表页复用,它就不再依附于card__header,类名却还带着冗余父级语义,变成user-list__header__title?还是硬塞进card命名空间里?怎么都不对
正确替代方案有哪些
面对“标题里的图标”“按钮里的加载指示器”这类视觉嵌套,BEM 给出明确解法,不是妥协命名,而是重构职责:
- 图标作为标题的视觉变体 → 用修饰符:
card__title card__title--with-icon - 图标具备独立语义(如状态徽标、操作入口)→ 升格为新 Block:
badge或icon-button,再组合使用:<card__title><badge badge--status="draft"></badge></card__title> - 真需微调子元素样式(比如 icon 在 title 内部的对齐)→ 用属性选择器:
[data-icon-position="left"],而非靠类名层级绑定
SCSS 里怎么避免误写出 block__el1__el2
SCSS 嵌套本身不违反 BEM,但一不留神就会生成非法结构:
- ✅ 安全写法(单层 &__):
.card { &__title { font-size: 1.2rem; } &__header { padding: 1rem; } }编译后是.card__title和.card__header - ❌ 危险写法(带空格 = 后代选择器):
.card { &__header { .icon { ... } } }编译后是.card__header .icon,既没遵循 BEM 命名,又引入隐式依赖 - ⚠️ 特别注意:
&__header__icon这种写法在 SCSS 里能编译通过,但生成的类名card__header__icon是 BEM 非法命名,CI 或团队 lint 规则应直接拦截
真正难的不是记住规则,而是每次写 __ 时,都得问一句:这个东西,是不是真的只属于当前 block?还是它其实早该自立门户?











