直接拼接bem类名易出错,因&会带入完整父链导致非法结构;可靠方案是显式传入块名的@mixin bem($block),并通过element()和modifier()声明子级,禁用&手动拼接。

为什么直接拼接BEM类名容易出错
在 Sass 中用字符串拼接生成 BEM 类名(比如 #{&}__item)看似简洁,但实际会破坏嵌套上下文——尤其当父选择器本身带条件(如 .is-active 或媒体查询)时,& 会把整个父链带进来,生成类似 .menu.is-active__item 这种非法 BEM 结构。BEM 要求修饰符必须作用于块级元素本身,而非其子元素。
@mixin bem() 必须接收 $block 参数
一个可靠的 @mixin bem 不能依赖 & 推导块名,而应显式传入块名。否则无法支持多层嵌套、混合块名或动态块(如组件库中 $block: $c-button)。常见错误是写成无参 @mixin element,导致复用时必须重复写块名,反而降低效率。
推荐写法:
@mixin bem($block) {
.#{$block} {
@content;
@mixin element($name) {
&__#{$name} {
@content;
}
}
@mixin modifier($name) {
&--#{$name} {
@content;
}
}
}
}
调用时明确传入块名:
@include bem('card') {
padding: 1rem;
@include element('header') {
font-weight: bold;
}
@include modifier('compact') {
padding: 0.5rem;
}
}
避免在 @content 中使用 & 引发选择器污染
很多人在 @content 里继续写 &__title,以为能自动挂载到当前块下,结果生成的是 .card .card__title(多了一个空格),而不是预期的 .card__title。这是因为 & 在 @content 作用域中仍指向外层 .card,但 Sass 不会自动“扁平化”它。
正确做法是:所有子级结构都通过 @include element() 或 @include modifier() 声明,不手动拼接 &。如果真要写组合选择器(如 .card__item.is-active),应单独定义修饰符,而不是在 element 内部硬编码。
- ✅ 正确:
@include element('item') { @include modifier('active') { ... } } - ❌ 错误:
@include element('item') { &.is-active { ... } }(生成.card__item.is-active是对的,但语义错位;且若后续加了@media,&会把媒体查询也带进去)
生产环境要考虑 CSS 输出体积和调试友好性
这套 @mixin 本质是语法糖,编译后仍是扁平 CSS。但要注意两点:一是嵌套过深(比如三层 element 套 modifier)会让源码可读性下降;二是开发者工具里看到的选择器是最终结果(如 .card__header),但找不到对应 Sass 行号——因为 @content 是动态注入的。
建议:
- 限定最多两层嵌套:块 → 元素 或 块 → 修饰符,避免
element内再套element - 在
@mixin bem()开头加// @group #{$block}注释,方便 IDE 折叠和搜索 - 禁用
compress模式输出,保留换行和缩进,否则调试时无法对应源文件位置
真正难的不是写出这个 @mixin,而是团队约定好什么时候该用它、什么时候该手写——比如原子类、工具类或第三方组件覆盖样式,就不该塞进 bem() 里。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











