SCSS中&__element编译为.card .card__title而非.card__title,因&代表完整父选择器且嵌套默认按后代关系拼接;BEM要求扁平类名,须用@at-root #{&}__xxx生成。

SCSS里&__element为什么编译出.card .card__title而不是.card__title
因为&代表的是当前完整选择器字符串,不是“块名”。在.card { &__title { } }中,&确实是.card,但SCSS嵌套机制默认把&__title当作“后代关系”处理,拼接时保留空格,结果就是.card .card__title。BEM要求的是无空格的单类名.card__title,否则样式依赖DOM层级,一旦HTML去掉外层.card,.card__title就完全失效。
@at-root #{&}__title是唯一安全生成元素类名的方式
必须跳出嵌套上下文,在CSS根层级生成目标类名。只有@at-root配合插值能剥离空格、确保输出扁平。
-
.card { @at-root #{&}__header { color: #333; } }→ 输出.card__header -
.card { @at-root #{&}--loading { opacity: 0.7; } }→ 输出.card--loading - 块名用变量时:
$block: "button"; @at-root #{$block}__icon { }→.button__icon - 禁止在
&:hover、&[disabled]或.card .header这类含空格父选择器内使用@at-root #{&}__xxx,否则&取到的是带空格字符串,插值后仍非法
所有__和--必须与块同级,不能嵌套进子容器或伪类
BEM的__和--是命名约定,不是结构派生。它们的CSS规则逻辑上必须和块定义处于同一层级,否则语义错乱、复用困难、调试崩溃。
- ✅ 正确:
.button { @at-root #{&}__icon { } @at-root #{&}--primary { } } - ❌ 错误:
.button { &:disabled { &__icon { } } }→ 编译为.button:disabled__icon,浏览器直接忽略 - ❌ 错误:
.card { .header { &__title { } } }→ 输出.card .header__title,违背BEM语义 - 真实项目中最容易被忽略的是:HTML漏写基础类(比如只加
card__title不加card),样式失效却查不到原因——DOM上少一个class,比JS逻辑错误更难定位
循环生成BEM类名时,&在@each里依然指向外层块,不是中间名
&在循环内部不会自动切换成当前迭代项;它始终绑定于循环外最近的父选择器。想生成.card__header、.card__footer,不能靠.card { @each $part in ("header", "footer") { &__#{$part} { } } },这仍会产出带空格的后代选择器。
立即学习“前端免费学习笔记(深入)”;
- ✅ 推荐写法:
$block: "card"; @each $part in ("header", "footer") { .#{$block}__#{$part} { } } - ✅ 或显式提级:
.card { @each $part in ("header", "footer") { @at-root #{&}__#{$part} { } } } - ⚠️ 注意:
@for不适合元素名遍历——它产生数字索引,而BEM元素名必须是语义化字符串,优先用@each
@at-root编译完全正确,只要HTML没同步写全card card--featured card__title card__title--large,整个链路就断了。


















