直接用 sass 嵌套写 &__element 会生成带空格的后代选择器(如 .card .card__title),违背 bem 扁平命名原则;正确做法是用 @at-root #{&}__element 显式提级生成无空格类名。

直接用 Sass 嵌套写 &__element 会生成带空格的后代选择器(如 .card .card__title),完全违背 BEM 扁平命名原则——这不是 bug,是 Sass 嵌套机制的默认行为。
为什么 &__item 在嵌套里编译出空格?
Sass 的嵌套规则是:子选择器自动前置空格,& 只做字符串替换,不校验语义。哪怕你写成 .card { &__title { } },Sass 仍按「.card 下的 &__title」解析,最终输出 .card .card__title(注意中间空格)。BEM 要的是无空格的 .card__title,必须显式提级。
- 空格意味着后代关系,破坏 BEM 的独立性与低权重优势
-
&不会“记住”你想要的是块名,它只忠实地拼接当前作用域的完整选择器字符串 - 一旦父选择器含伪类(如
.btn:hover),&__text就变成.btn:hover__text,CSS 语法非法
怎样用 @at-root + #{&} 安全生成 .block__element?
这是目前最轻量、最可控的解法:跳出嵌套流,手动拼接并提级到 CSS 根层。关键不是“省事”,而是确保输出类名 100% 符合 BEM 字符串规范。
- 正确写法:
.card { @at-root #{&}__title { color: red; } }→ 输出.card__title - 若块名来自变量(如
$block: "button"),改用@at-root #{$block}__icon - 禁止在
@at-root内再嵌套&__subitem,那会产出.card__title__icon,BEM 不允许三层结构 - 所有
--modifier同理:@at-root #{&}--compact { padding: .5rem; }
用 @mixin 封装 b()/e()/m() 时要注意什么?
封装能提升一致性,但混合宏(mixin)本身不解决语义问题——它只是把重复逻辑收口,错误用法照旧翻车。
-
@mixin e($el)必须在块定义内部调用,且内部必须用@at-root #{&}__#{$el},否则&指向错位 - 命名空间(如
$namespace: "el")要统一配置,避免不同组件用不同前缀导致样式隔离失效 - 不要在
@mixin m()里写&__item--disabled,修饰符只能挂块或元素本身,不能挂元素之下 - 多个修饰符并存(如
.btn--primary.btn--large)必须分开声明,不可合并为&--primary--large
最容易被忽略的兼容性陷阱
某些 Dart Sass 版本对 @supports + & 插值组合解析不稳定;更隐蔽的是函数式伪类(如 :nth-child(2))或属性选择器(如 [data-id])作为父选择器时,&__item 会产出非法字符串(如 [data-id]__item),浏览器直接丢弃。
- 凡含
:not()、:nth-、[attr]的父选择器,一律禁用&__拼接 - 写完 Sass,必须
grep -n " \." output.css检查是否有空格分隔的选择器 - 用 VS Code 或 WebStorm 开启 Sass 语法高亮,能快速发现悬空的
&或缺失的@at-root
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











