sass 的 & 符号是父选择器严格复读器,非自动拼前缀工具;在 bem 中仅允许用于 &__element、&--modifier、&:pseudo 三种模式,误用会导致空格选择器或语义错乱。

Sass 的 & 符号在 BEM 场景下不是“自动拼前缀工具”,而是“原样复读父选择器”的严格映射器——用错一次,CSS 类名就偏离语义,且极难被肉眼发现。
为什么 &__item 会编译出带空格的选择器
这是 Sass 嵌套的默认行为,不是 bug。当你写:
.card {
&__title {
color: red;
}
}
它实际编译为 .card .card__title(注意中间空格),因为 & 在嵌套块内代表“当前规则的完整选择器”,而嵌套本身已隐含后代关系。
这直接违反 BEM 原则:元素类名必须是扁平、无空格、独立可复用的单类名。
- ✅ 正确目标:生成
.card__title - ❌ 错误结果:
.card .card__title(权重升高、依赖层级、无法单独使用) - ? 解法只有两种:
@at-root #{&}__title或直接手写.card__title
& 只允许出现在三种合法 BEM 模式中
超出以下任一模式,& 就是在制造维护陷阱:
-
&__element:必须紧贴块级声明,且不能跨层,如.card { &__body { ... } }→.card__body -
&--modifier:只能修饰块自身,如.card { &--compact { ... } }→.card--compact -
&:hover、&:focus等伪类:仅用于显式状态控制,且需配合 JS 切换类,禁用&:hover__icon这类非法组合
其他写法一律禁用:& .child、&__item a、&__header &--large —— 它们要么产出空格选择器,要么语义错位(比如把修饰符挂到元素上)。
嵌套中混用 & 和标签/属性选择器必然翻车
& 不区分语义,只做字符串复读。一旦父选择器含伪类或属性,& 就会照单全收:
.btn {
&:hover {
&__icon { opacity: 0.8; }
}
}
编译结果是 .btn:hover .btn__icon,但 BEM 要求的是 .btn--hover .btn__icon 或更合理的结构 —— 这里 & 已经把语义从“状态类”偷换成“伪类依赖”,破坏了可测试性和 HTML 解耦。
- ⚠️ 风险点:
&:disabled__text会产出非法 CSS(浏览器直接忽略) - ? 正确路径:状态统一用修饰符类,如
.btn--disabled,由 JS 控制增删 - ? 检查手段:grep 编译后 CSS,搜索
:后紧跟__或--,凡有即错
用 mixin 封装 BEM 时,& 必须和 @content 绑定使用
拆分 b()、e()、m() 三类 mixin 是唯一可控方案。关键在于:e() 内部必须用 &__#{$element},而非拼接字符串 —— 否则会丢失嵌套上下文,导致 .button__icon 变成孤立的 .icon。
@mixin e($element) {
&__#{$element} {
@content;
}
}
.button {
@include e(icon) {
width: 16px;
}
}
这段能正确产出 .button__icon;若写成 .#{$B}__#{$element},$B 就可能为空或错值,且无法响应外层嵌套变化。
真正容易被忽略的点:所有 e() 和 m() 必须定义在 b() 块内部调用,否则 & 指向失效 —— 这不是语法错误,而是运行时类名错乱,上线后才暴露。











