bem禁止.card__header .card__title这类后代选择器,因其破坏单类名定位原则,导致样式强耦合dom结构、复用困难、匹配开销增大;同时禁止.card__header__title三层命名,因element必须直属于block,否则语义越界、无法抽离复用。

为什么BEM禁止.card__header .card__title这类后代选择器
因为这直接破坏BEM的封装前提:样式必须靠单类名定位,而非DOM层级。一旦写.card__header .card__title,浏览器就得从所有.card__title里反向查找父节点是否为.card__header,匹配开销翻倍,且HTML加个<div class="wrapper">就断样式。
<p>常见错误现象:</p>
<ul>
<li>SCSS里写<code>.card { &__header { &__title { } } },编译出.card__header .card__title,看似“嵌套”,实则退化成传统CSS
<h2 class="card__title"></h2>挪到<section></section>里,样式瞬间消失,DevTools里找不到原因.card__header里加padding,B在.card__title里加margin,谁生效取决于加载顺序
card__header__title为什么是非法命名
它不是手误,而是语义越界:card__header只是card的一个Element,不能充当Block去承载另一个Element。BEM只允许Block__Element或Block--Modifier两级结构,三层即违规。
后果很实际:
- 无法抽离复用——
card__header__title拿不到用户页或弹窗里用 - 工具链失效——
stylelint-selector-bem-pattern会报错,IDE跳转找不到定义位置 - SCSS嵌套生成非法名:
&__header { &__title { } }编译后就是.card__header__title,但这个类名本身不被BEM认可
遇到真实孙子结构该怎么写
DOM可以深,类名必须平。关键不是“怎么命名孙子”,而是“这个节点是否值得独立成Block”。
- 如果
<h2></h2>在多个地方复用(卡片、弹窗、侧边栏),就定义为独立Block:.heading+.heading--in-card-header - 如果它只在卡片头部出现且样式强依赖上下文,用Modifier+父级微调:
.card__header .heading只控制margin-bottom等布局属性,核心样式(font-size、color)仍由.heading自己管 - 极少数场景(如折叠面板里的二级标题),用状态类:
<h3 class="heading is-subheading"></h3>,配合.is-subheading { font-size: 0.9em; }
容易被忽略的边界点
最常踩的坑不是不会写__和--,而是在加DOM节点时没停下来问一句:“这个节点是不是一个能独立存在、有明确功能边界的UI单元?”
如果不是,它就不该有自己的BEM类名——更不该靠多一个__来“假装”合理。BEM真正的成本不在命名规则上,而在每次写HTML前那半秒的语义判断。











