@at-root #{&}__element是唯一安全生成BEM类名的方式,纯嵌套会产出带空格的后代选择器,违背扁平化原则;因&代表完整父选择器字符串,直接&__title编译为.card .card__title,而BEM要求无空格单类名.card__title。

直接说结论:@at-root + & 插值是唯一安全生成 __ 和 -- 类名的方式,纯嵌套会产出带空格的后代选择器,完全违背 BEM 扁平化原则。
为什么 &__title 会编译出错误选择器
SCSS 的 & 默认拼接的是“当前完整选择器字符串”,不是“块名”。在 .card { &__title { ... } } 中,& 确实是 .card,但编译结果仍是 .card .card__title(带空格),因为 SCSS 嵌套天然表达层级关系。BEM 要求的是单类名 .card__title,而非后代选择器。
- 空格意味着 DOM 依赖,一旦 HTML 改成
<div class="card__title">外层没有.card,样式就失效 - 若写在子选择器里,比如
.card { .header { &__title { } } },&变成.card .header,结果是.card .header__title,语义彻底错乱 -
&:hover { &__icon { } }会生成非法类名.card:hover__icon,浏览器直接忽略
@at-root #{&}__element 是唯一可靠写法
必须跳出嵌套上下文,在 CSS 根层级生成目标类名。这是目前最稳定、可预测的方式。
- 正确示例:
.card { @at-root #{&}__header { color: #333; } }→ 输出.card__header - 修饰符同理:
@at-root #{&}--loading { opacity: 0.7; }→.card--loading - 变量也适用:
$block: "button"; @at-root #{$block}__icon { ... } - 禁止在
@media、&:focus或任何子选择器内部再用@at-root #{&}__xxx,否则输出顺序不可控,且破坏 BEM 层级语义
元素和修饰符必须与块同级,不能嵌套进状态或容器
BEM 的 __ 和 -- 是扁平命名,不是结构派生。它们的 CSS 规则必须和块定义处于同一逻辑层级。
立即学习“前端免费学习笔记(深入)”;
- ✅ 正确:所有
__和--都写在块选择器顶层,用@at-root提级 - ❌ 错误:
.button { &:disabled { &__icon { } } }—— 这试图让元素依赖状态,违反 BEM “修饰符增强,不替代基础类”原则 - 真实场景中,
.button__icon的悬停变色应单独写:.button__icon:hover { ... },或用@extend复用状态,而不是在伪类里再造元素 - 漏掉基础类(如只写
card__title--large不写card__title)会导致样式失效,且调试时极难定位——DOM 上少一个 class,比逻辑错误更隐蔽
真正容易被忽略的是:BEM 的解耦效果不来自语法糖,而来自对类名显式归属的强制约束。哪怕 SCSS 编译正确,如果 HTML 没同步写全 card card--featured 和 card__title card__title--large,整个链路就断了。命名规范最终靠人落地,工具只是校验器。


















