嵌套越多,.card__title越难被覆盖,因为错误嵌套(如.card { .card__title {} })生成高权重选择器.card .card__title(0-2-0),而正确用&__title输出单类名.card__title(0-1-0);多一个空格即多一单位权重,BEM失效实为人为加权。

为什么嵌套越多,.card__title越难被覆盖
嵌套本身不提高优先级,但会悄悄生成高权重选择器。比如写成 .card { .card__title { color: red; } },编译后是 .card .card__title(权重 0-2-0),而正确用 &__title 应该输出 .card__title(权重 0-1-0)。多一个空格,权重就翻倍——这不是 BEM 失效,是你在编译前就亲手加了枷锁。
& 前有没有空格,决定你是拼接还是嵌套
& 永远只代表上一层的完整选择器字符串,不是“视觉缩进的父级”。它容易误读,导致权重失控:
- 正确:
.card { &__header { } }→ 编译为.card__header - 错误:
.card { .header { &__title { } } }→ 编译为.header__title(&指向.header,不是.card) - 更危险:
.card { .header { & __title { } } }(中间有空格)→ 编译为.header .card__title,完全错乱
检查点:按住 Ctrl 点击编译后的 CSS 文件,直接看生成的选择器,比猜更准。
嵌套超 3 层时,删掉最内层比补 & 更有效
浏览器匹配选择器是从右往左查的。.layout .sidebar .nav .item .link 这种 5 层后代选择器,会让引擎对每个 .link 都逐层向上验证父级是否存在——DOM 越深、更新越频繁,卡顿越明显。Lighthouse 报 “Avoid overly specific selectors” 就源于此。
立即学习“前端免费学习笔记(深入)”;
- 真实重构动作:
.card { .header { .title { } } }直接删掉.title这层,改用.card__title+@at-root提级 - 检查编译后 CSS:如果出现超过 4 个 class 连续空格(如
.a .b .c .d),说明嵌套已失控 - stylelint 配置
max-nesting-depth: 3可自动拦截
@at-root 不是逃逸键,是语义提级开关
当某段样式逻辑上属于当前组件、但不该继承嵌套路径时,必须显式提级,否则产出的就是带毒冗余选择器:
- 错误:
.modal { .header { @extend %text-center; } }→ 输出.modal .header .u-text-center - 正确:
.modal { @at-root .u-text-center { text-align: center; } }→ 输出.u-text-center - 可控提级:
@at-root (with: .theme-dark) { .button { color: white; } }→ 输出.theme-dark .button - 特别注意:
@media块内禁用&嵌套;@at-root (with: media)才能保证媒体查询规则平级输出,不抬升权重
真正难的不是让 & 多嵌两层,而是删掉那一层后,HTML 是否仍能准确匹配——如果删了嵌套,.card__title 规则还生效,说明你已经走在正确路上;如果失效,问题不在 Sass,而在 HTML 缺失基础 Block 类名。


















