@at-root #{&}__element是唯一安全生成bem类名的方式,因&默认代表完整父选择器字符串,直接&__title会编译为.card .card__title(后代选择器),违背bem扁平化要求;空格导致dom层级依赖,html缺失外层类则样式失效。

为什么&__item会编译成后代选择器而不是单类名
SCSS 的 & 代表“当前完整选择器字符串”,不是“块名”。在 .card { &__title { } } 中,& 确实是 .card,但嵌套机制天然加空格,结果是 .card .card__title —— 这是合法 CSS,但违背 BEM 要求的单类名扁平结构。
空格意味着 DOM 层级依赖:如果 HTML 只写 <div class="card__title">,外层没 <code>card 类,样式完全不生效。
更危险的是伪类或复合父选择器场景:
- .button { &:hover { &__icon { } } } → 编译为 .button:hover__icon(非法类名,浏览器忽略)
- .sidebar .widget { &__header { } } → .sidebar .widget__header(语义错乱,BEM 只认 .widget__header)
@at-root #{&}__element 是唯一可靠提级方式
@at-root 强制跳出当前嵌套上下文,把规则输出到 CSS 根层级;#{&} 插值则只取 & 的字符串值(不含空格),再拼接 __ 或 --。
正确写法示例:
.card {
@at-root #{&}__header { color: #333; }
@at-root #{&}--loading { opacity: 0.7; }
}
→ 输出 .card__header 和 .card--loading,无空格、无嵌套、符合 BEM。
变量也适用:
- $block: "button"; @at-root #{$block}__icon { } → .button__icon
- 修饰符同理:@at-root #{&}--disabled
⚠️ 禁止在 @media、&:focus 或子选择器内部再用 @at-root #{&}__xxx,否则输出顺序不可控,且破坏 BEM 语义层级。
为什么不能在嵌套里循环生成元素类
BEM 的 __ 和 -- 必须与块同级,不是结构派生。它们的 CSS 规则必须和块定义处于同一逻辑层级。
错误示范:
-
.button { &:disabled { &__icon { } } }→ 把元素绑定到状态上,违反 “修饰符增强,不替代基础类” 原则 -
.card { .header { &__title { } } }→ 产出.card .header__title,语义彻底偏离 -
.container { @for $i from 1 through 12 { .col-#{$i} { } } }→ 生成.container .col-3,类名失去全局可调用性
真正可用的自动化路径只有显式提级 + 插值,或工具链配合。预处理器无法“自动推导”BEM 结构,所有看似自动的拼接(如 &__content)默认产出带空格的后代选择器。
容易被忽略的 HTML 同步问题
即使 SCSS 编译完全正确,只要 HTML 漏掉基础类,整个 BEM 链路就断了。
例如:
- 正确 HTML:<div class="card card--featured">
<h2 class="card__title card__title--large">
<br>
- 错误写法:<code><h2 class="card__title--large"></h2>(缺 card__title,样式失效)
这种问题调试极难定位——DOM 上少一个 class,比逻辑错误更隐蔽。BEM 的解耦效果不来自语法糖,而来自对类名显式归属的强制约束。











