直接用&__item会失效,因&仅代表当前选择器的直接父级而非任意祖先;正确做法是每个Block独立声明,子元素和修饰符均用&__或&--派生,禁止跨Block嵌套或重复写全类名。

为什么直接用 & 写 &__item 会失效
因为 & 只代表**当前选择器的直接父级**,不是“任意上层祖先”。比如你在 .header-nav 规则里写 &__link,Sass 会编译成 .header-nav__link,而不是你想要的 .header__link——这违反了 BEM 中“元素必须严格归属某个 Block”的语义约束。
常见错误现象:
- 样式没生效,检查生成的 CSS 类名发现前缀错位
- 嵌套层级一深,
&就开始“指错爹” - 在伪类(如
:hover)、兄弟选择器(如~)里误用&__xxx,产出非法类名
怎样用 & 正确生成 .block__element 和 .block--modifier
核心原则:每个 Block 必须独立声明,所有子元素和修饰符都从它出发派生,不跳级、不跨 Block。
正确写法示例:
立即学习“前端免费学习笔记(深入)”;
@include b(card) {
// 生成 .card
&__title { /* .card__title */ }
&__image { /* .card__image */ }
&--featured { /* .card--featured */ }
&__image--rounded { /* .card__image--rounded */ }
}关键点:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
-
&__xxx只能在顶层@include b(xxx)块内使用 - 禁止在
&:hover或&.is-active里再写&__xxx,否则变成.card.is-active__xxx - 需要组合状态 + 元素时(如 hover 下的某个元素),应改用独立规则或
@at-root提升作用域
什么时候必须放弃 &,改用 @mixin e() 和 @mixin m()
当你要在修饰符内部、伪类、兄弟选择器等非顶层上下文中引用元素或修饰符时,& 的绑定就不可靠了。这时混入(mixin)是唯一可控的方式。
典型场景:
-
&--dark { @include e(content) { color: white; } }→ 输出.block--dark .block__content,而非.block--dark__content -
&:hover { @include e(button) { opacity: 0.8; } }→ 正确生成.block:hover .block__button -
&__header + &__footer { margin-top: 1rem; }→ 这里&指向的是同一个父级,但两个&__xxx是并列关系,不能靠单个&表达
基础 mixin 定义(精简版):
$element-separator: '__';
$modifier-separator: '--';
<p>@mixin e($name) {
@at-root {</p><h1>{& + $element-separator + $name} { @content; }</h1><p>}
}</p><p>@mixin m($name) {
@at-root {</p><h1>{& + $modifier-separator + $name} { @content; }</h1><p>}
}命名空间和分隔符怎么统一管理才不翻车
硬编码 __ 和 -- 在多个文件里散落,后期想换分隔符或加项目前缀(如 el-xxx)时,改起来就是灾难。
推荐做法:
- 把分隔符和命名空间定义为全局变量,放在
_variables.scss里,用!default保证可覆盖 - 所有
@mixin b()、e()、m()都读取这些变量,不写死字符串 - 避免在组件样式中直接拼接
.#{$ns}-#{$block}__#{$elem},全部走 mixin 封装 - 如果项目已用 Vue / React,建议同步导出这些变量到 JS,保持类名字符串一致(比如用
sass-export或自定义 Vite 插件)
最容易被忽略的一点:Sass 的 & 是静态解析的,它不感知运行时结构。哪怕你用 v-for 动态生成多个 Block 实例,Sass 编译时也只认你写的那一层嵌套逻辑——所以别指望靠多层嵌套自动推导出正确的 BEM 路径。

















