scss中&__content在块级顶层使用时编译为.card__content(正确),但若嵌套在子选择器内(如.card { .header { &__title {} } })则生成.card .header__title(错误),违背bem扁平化要求;必须严格限定__和--仅出现在块作用域顶层,禁用多层嵌套。

SCSS里直接用&__content会生成错误选择器
编译结果是 .card .card__content(带空格),但BEM要求的是 .card__content(无空格、扁平类名)。这是因为 SCSS 默认把嵌套当作后代关系处理,& 在 .card { &__content { ... } } 中被解析为 “当前完整选择器 + __content”,而当前选择器就是 .card,所以拼出来是 .card__content —— 等等,这好像没错?
问题出在你写的位置:如果你把 &__content 写在了子选择器内部,比如:.card { .header { &__title { ... } } },那 & 就代表 .card .header,拼出来就是 .card .header__title,完全偏离 BEM。BEM 要求所有元素/修饰符都直接挂载在块名下,不能有中间层级。
正确做法只有一种:所有 __ 和 -- 必须出现在块级作用域顶层,且紧贴 &:
-
.card { &__header { ... } &--collapsed { ... } }✅ 编译为.card__header、.card--collapsed -
.card { .header { &__title { ... } } }❌ 编译为.card .header__title,不是 BEM 元素 -
.card { &:hover { &__icon { ... } } }❌ 编译为.card:hover__icon,非法类名
为什么&--primary能用,但&:hover__text不行
&--primary 是安全的,因为 & 此时代表块名(如 .button),拼出来是 .button--primary,符合 BEM 修饰符定义;而 &:hover__text 这种写法根本不会通过编译 —— :hover 是伪类,__text 是字符串,SCSS 不允许伪类后直接跟双下划线。
更常见的是误写成:.button { &:hover { &__text { ... } } },这会生成 .button:hover__text(一个非法类名),浏览器直接忽略。BEM 的元素必须是独立 class,不能靠状态“派生”。
真实项目中该怎么做:
- 元素(
__)和修饰符(--)永远与块同级,不嵌套进任何伪类、媒体查询或子容器内 - 状态样式(如
:hover、:disabled)可以写在块内,但只能用&:hover或&[disabled],不能在里面再用&__xxx - 如果需要“按钮悬停时图标变色”,应写成:
.button__icon { @extend .button:hover &; color: blue; },而不是在&:hover里嵌套&__icon
@at-root不是补救手段,而是破坏顺序的信号
有人发现 .card { @at-root #{&}__footer { ... } } 能强行输出 .card__footer,就以为这是“兜底方案”。其实它只是绕过嵌套逻辑,把样式提级到全局,代价是 CSS 输出顺序不可控。
尤其当多个 SCSS 文件用 @import 组织时,@at-root 会让本该按文件顺序出现的 .card--dark 跑到 .card__body 前面,导致权重覆盖失效(比如 .card--dark 本该覆盖默认背景,却因位置靠前被后面声明的 .card__body 覆盖)。
真正该做的,是约束书写习惯:
- 每个 BEM 块定义必须是单一层级:块名、所有
__元素、所有--修饰符,全部平铺在{}里 - 禁止在
&__xxx内部再写嵌套;禁止在&--xxx里写&:hover - 变量驱动的块名(如
$block: "modal")必须配插值:@at-root #{$block}__close,但仅限于动态场景,静态块坚决手写全名
深层嵌套+&等于隐性耦合,上线后难排查
像 .sidebar { .widget { &__title { ... } } } 这种写法,看着结构清晰,实际编译出 .sidebar .widget__title —— 它依赖 HTML 中 .widget 必须在 .sidebar 内,一旦组件复用到非 sidebar 场景,样式就丢失。BEM 的核心是“类名即语义”,不是“路径即样式”。
更隐蔽的问题是选择器权重:编译后的 .sidebar .widget__title 权重是 0,1,2,而纯 .widget__title 是 0,1,0,前者更容易意外压制其他样式,调试时你会看到明明写了 .widget__title { color: red },却还是黑色,最后发现是某处 .sidebar .widget__title 在后面定义了 color: black。
所以,哪怕为了快速开发,也请守住这条线:
- 所有 BEM 类名必须能脱离父容器独立存在
- HTML 中该加什么 class,CSS 里就写什么 class,不靠嵌套“猜”结构
- 真要处理容器上下文(如暗色模式),用属性选择器:
[data-theme="dark"] .widget__title,而不是靠嵌套层级
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











