sass中bem类名必须扁平无空格,&编译后代选择器;安全写法为@at-root #{&}__content或手动写全名;修饰符需与块同级;@each循环须用@at-root提级或变量拼接;分隔符需显式管理。

直接写 &__content 会编译出后代选择器,不是 BEM 要的扁平类名
很多人以为 .card { &__content { } } 能生成 .card__content,结果实际输出是 .card .card__content(带空格),变成后代选择器。这不是 bug,而是 Sass 嵌套的默认行为:& 只负责拼接字符串,空格照单全收。
真正安全的写法只有两种:
- 用
@at-root #{&}__content强制提级,确保输出.card__content - 手动写全名:
.card__content,彻底绕过嵌套歧义
尤其注意:VS Code 自动补全、复制粘贴时容易混入不可见空格,&__content 和 & __content(中间有空格)编译结果完全不同,后者一定失效。
&--modifier 必须和块同级,不能嵌在 &:hover 或子选择器里
修饰符语义上是独立状态标识,不是“某个状态下派生的子类”。写成 .btn { &:hover { &--active { } } } 会产出非法类名 .btn:hover--active,浏览器直接忽略。
正确做法是把修饰符提至块顶层:
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
-
.btn { &--primary { } &--disabled { } }→ 编译为.btn--primary、.btn--disabled - 元素级修饰符也必须扁平:
&__icon--spin,而不是&__icon { &--spin { } } - 如果需要“悬停时图标变色”,应写
.btn__icon { @at-root .btn--hover & { } },而非在&:hover内部再嵌套
循环生成 BEM 类名时,& 会指错对象,必须用 @at-root + 插值
@each 里直接写 &__#{$part} 看似省事,但 & 仍指向外层块选择器,导致输出 .card .card__header(带空格),违背 BEM 扁平要求。
可靠方案只有:
- 显式用
@at-root #{&}__#{$part}提级,强制拼接 - 或更稳妥地定义变量:
$block: "card",然后写.#{$block}__#{$part},完全脱离&的上下文干扰 - 修饰符列表必须是字符串字面量:
@each $mod in ("large", "disabled"),写成(large, disabled)(无引号)会报Undefined variable "$large"
BEM 的双下划线 __ 和双破折号 -- 不是语法糖,Sass 不识别它们
Sass 把 __ 和 -- 当作普通字符,不提供任何特殊解析。你不能指望它自动理解“这是 BEM 分隔符”,所有拼接都得靠人控制。
常见硬伤:
- 连字符参与运算时易被误解析:
.#{$block}__#{$element}-active中的-active可能被当减法;应写成.#{$block}__#{$element}#{"-active"} - 推荐统一管理分隔符:
$element-separator: '__'、$modifier-separator: '--',避免硬编码和项目内不一致 - 伪类、媒体查询、属性选择器里的
&会带上完整前缀(如.sidebar .widget:hover),此时&__item会生成.sidebar .widget:hover__item—— 这种类名根本不存在,样式必然失效
最易被忽略的一点:BEM 要求所有 __ 和 -- 类名必须与块同级、扁平、无空格;一旦它们出现在任何嵌套层级内部(哪怕只多一层 .card { .body { ... } }),就已偏离规范,后续维护成本会指数上升。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










