sass 的 & 仅字符串拼接,不识别 bem 语义,.card { &__title { } } 输出 .card .card__title(含空格);应使用 @at-root #{&}__element 或 @mixin bem($block) 显式控制命名结构。

为什么&__element会编译出带空格的选择器
Sass 的 & 只做字符串拼接,不识别 BEM 语义。写 .card { &__title { } },实际输出是 .card .card__title(中间有空格),因为换行、缩进或不可见 Unicode 空格(比如 VS Code 自动补全混入的零宽空格)都会被原样保留。& 不会“跳过空格”或“自动扁平化”,它只是把父选择器字符串 + __title 拼一起——而 .card 后面那个换行符,在 Sass 解析时就等价于空格。
@at-root #{&}__element 是最稳妥的写法
想确保生成 .card__title 这种单类名,必须跳出嵌套上下文再拼接。用 @at-root 强制提层,配合插值 #{&} 获取当前块名:
@include bem('card') {
@at-root #{&}__header {
font-size: 1.2rem;
}
}
这样无论嵌套多深、有没有条件类(如 .card.is-active),#{&} 都只取块名本身,不会带入伪类或状态修饰符。
- 禁用
&__header直接写法,90% 场景下都会翻车 - 避免
& __header(中间有空格),肉眼难辨但必出错 - 不要依赖 IDE 补全的
&__片段,它不校验语义
修饰符&--modifier必须和块同级
&--modifier 只能在块一级使用,不能塞进元素或伪类里。错误写法:.card { &__body { &--scrollable { } } },结果是 .card__body--scrollable —— 这违反 BEM,修饰符不该作用于 element,而应作用于 block 本身(即 .card--scrollable)。
- 正确位置:
.card { &--compact { padding: 0.5rem; } } - 禁止在
&:hover内写&--active,否则产出.card:hover--active(非法类名) - 多个 modifier 要并列声明,不嵌套:
&--primary和&--large必须同级
用@mixin bem($block)代替&拼接
显式传参比依赖 & 更可靠。一个基础 @mixin bem 能封住所有常见翻车点:
@mixin bem($block) {
.#{$block} {
@content;
@mixin element($name) {
&__#{$name} { @content; }
}
@mixin modifier($name) {
&--#{$name} { @content; }
}
}
}
调用时强制你明确块名:@include bem('button') { @include element('icon') { ... } }。这能防住两类问题:
- 父选择器带媒体查询或状态类时,
&会把整个链带进来(如@media (min-width: 768px) { .btn { &__icon { } } }→@media (min-width: 768px) .btn__icon正确;但若用&__icon在嵌套里,可能变成@media (min-width: 768px) .btn .btn__icon) - 组件库中块名动态(如
$c-button),&无法推导,必须传参
BEM 不是语法糖,是约束。靠 & 偷懒,最后得花三倍时间 grep 空格和检查 CSS 输出。真正省事的方式,是让块名、元素、修饰符的边界在 Sass 源码里就不可逾越。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











