唯一安全生成BEM类名的方式是@at-root #{&}__element,因&代表完整选择器而非块名,嵌套或拼接写法在层级变化、伪类或复用时必然崩坏。

盲目滥用嵌套会让 BEM 名存实亡——编译出的选择器里出现空格,就等于直接放弃 BEM 的全部优势。
嵌套不等于 BEM,& 缺失或空格错位立刻退化为后代选择器
SCSS 嵌套只是语法糖,真正决定输出是否符合 BEM 的是 & 的使用方式和空格有无:
-
.card { &__title { } }→ 编译为.card__title(✅ 正确) -
.card { .title { } }→ 编译为.card .title(❌ 后代选择器,权重高、不可复用) -
.card { &__header { .title { } } }→ 编译为.card__header .title(⚠️ 第二层又掉回 DOM 依赖,BEM 断层)
只要某一层没写 &,或中间多了一个空格,就等于主动把语义层级交还给 HTML 结构,BEM 的封装性瞬间归零。
嵌套过深会生成非法 BEM 类名,比如 card__header__title
BEM 明确禁止 Element 嵌套 Element:card__header__title 不是手误,是语义错误——card__header 本身只是 Element,不能充当 Block 角色:
- 它无法独立复用:抽离到其他页面时,
card__header__title失效,因为没有card__header容器 - 它破坏可维护性:改
card__header结构(如换成card__section),样式静默崩坏 - 它违背 BEM 核心约束:Element 必须直接隶属于 Block,路径只能是
block__element或block--modifier,不能递归
而这种类名恰恰容易在 .card { &__header { &__title { } } } 中悄悄产出,且 Stylelint 的 max-nesting-depth 完全检测不到。
嵌套掩盖了真正的组件边界,导致文件职责模糊
BEM 的块(Block)必须物理隔离,靠文件和 @use 强制划界。但嵌套滥用会让开发者误以为“缩进够深=封装到位”,结果:
- 在
_card.scss里写.page .card或article .card,把布局逻辑混进组件文件 - 跨文件引用样式变成隐式依赖,
@use "button"后却在_form.scss里继续嵌套.button,破坏模块自治 - 想加第四层嵌套时才发现:父级块不在当前文件,
&没东西可拼——这不是语法限制,是设计意图被嵌套表象绕开了
真正该警觉的不是缩进层数,而是编译后 CSS 文件里是否出现了空格。搜索 .card .title 这种字符串,比数大括号层数更能暴露问题。
工具链不会替你判断语义,只校验表面结构
Stylelint 的 max-nesting-depth: 3 默认对 SCSS 无效,必须配 customSyntax: 'postcss-scss' 和 stylelint-scss 插件;但它只数嵌套节点,不验证输出类名是否合法:
- 它放行
.card { &__header { .icon { } } }(编译出.card__header .icon,BEM 失效) - 它拦不住
.btn { &:hover { .label { } } }(.label不属于按钮语义,却是合法三层) - 它无法识别
button-icon--large这种缺双下划线的命名错误
所以,嵌套深度检查只是探测器,不是守门员。最终防线是你在写 { 和 } 之间,是否每一步都明确回答了:“这个类名,脱离当前 HTML 结构还能用吗?”
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











