直接用 &__element 拼接 bem 类名易翻车,因 & 仅展开父选择器字符串、不识别“块”概念,导致动态块名、嵌套条件、修饰符污染等非法类名;可靠方案是显式传参的 @mixin bem($block) 封装语义层级。

直接用 &__element 拼接 BEM 类名看似省事,但只要块名来自变量、嵌套带条件(如 .card.is-active)、或需要复用到不同上下文,就会生成非法类名(比如 .card.is-active__header),破坏 BEM 结构。可靠方案是显式传入块名,用 @mixin bem($block) 封装语义层级。
为什么 &__element 在真实项目里大概率翻车
Sass 的 & 只展开为最近一层父选择器的**完整字符串**,不感知 BEM 的“块”概念。它无法区分「当前组件的块名」和「外部传入的动态块名」。
- 写
.card { &__title { ... } }编译出.card__title—— 看似 OK,但一旦把.card抽成@include card() { ... },&就变成空或上层父链 - 在媒体查询里嵌套:
@media (min-width: 768px) { .card { &__title { ... } } }→ 编译出@media (min-width: 768px) {.card__title { ... }},没问题;但若父选择器是.card--compact,&__title就变成.card--compact__title,这违反 BEM —— 修饰符不能污染元素命名 - 多人协作时,有人写
&__icon,有人手写.card-icon,类名口径立刻失控
@mixin bem($block) 必须显式接收块名参数
块名不能靠 & 推导,也不能从父级 CSS 类自动提取——它必须由调用者明确声明,否则无法支持组件库、主题切换、多语言块名等工程场景。
- 禁止写无参
@mixin element():调用时还得重复写块名,比如@include element('card', 'header'),反而更啰嗦 - 允许传变量,但变量本身得是字符串字面量,比如
$c-button是"button",不是未定义的button - 若需加命名空间(如
xm-button),应在$block传入前拼好,或在 mixin 内部用全局$namespace拼接,不要在 mixin 里做str-replace类操作
推荐写法:
@mixin bem($block) {
.#{$block} {
@content;
@mixin element($name) {
&__#{$name} {
@content;
}
}
@mixin modifier($name) {
&--#{$name} {
@content;
}
}
}
}
修饰符必须独立挂载在块或元素上,不能靠嵌套推导
BEM 要求 --disabled 这类状态类必须直接作用于目标元素本身,而不是靠父级状态“传染”。否则 JS 控制、PurgeCSS 清理、样式隔离都会出问题。
- 错误写法:
.btn { &--large { &__icon { ... } } }→ 编译出.btn--large__icon,非法 - 正确写法:先声明元素,再在其上加修饰符:
@include element('icon') { @include modifier('disabled') { opacity: 0.4; } }→ 输出.btn__icon--disabled - 禁用
&--mod:hover这类组合:伪类应单独提层,用@at-root避免污染层级,比如@at-root .btn--disabled:hover { ... }
最易被忽略的一点:BEM 的 __ 和 -- 不是 Sass 语法,Sass 完全不识别它们——所有拼接逻辑都靠人写对。一旦混入变量、条件、循环,& 就不可信,只有显式传参 + 函数式拼接才能守住边界。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











