Sass嵌套本身不生成过多选择器,但不当使用@extend、&(空格误用)和@media嵌套会引发选择器爆炸、权重失控与重复输出。
直接说结论:Sass嵌套本身不生成“过多”选择器,但嵌套写法不当会触发@extend爆炸、&误拼、@media重复输出这三类问题,最终导致编译后CSS里出现大量冗余、不可控的选择器——不是嵌套太多,是嵌套时没盯住它到底在拼什么。
@extend不是复用样式,是把所有引用它的选择器合并成一个逗号分隔列表。一旦多个组件在不同嵌套层级里@extend同一个%placeholder,编译器就会把它们全拉平拼进去。
.card { @extend %flex-center; } .modal { @extend %flex-center; } .btn-group { @extend %flex-center; } → 编译出.card, .modal, .btn-group { display: flex; },看起来干净,但若%flex-center还被嵌套在.sidebar .nav里调用,就会变成.sidebar .nav .card, .sidebar .nav .modal, ...
@extend:如果%flex-center定义在_base.scss,而.card在_card.scss里@use了它,但.card又被_layout.scss里的.page .section .card嵌套调用,那@extend会把整个父链都带上%flex-center对应的实际声明,看前面有没有意外多出来的祖先类名&永远只代表上一层的完整选择器字符串,它不“记忆”缩进层级。有无空格,决定你是在拼接类名,还是在构造后代选择器——后者会悄悄把选择器变长、变重、变难覆盖。
.card { .header { &__title { } } } → 编译为.header__title(&指向.header,丢失.card上下文),后续想覆盖就得单独写.header__title,和.card完全脱钩.card { .header { .title { color: red; } } } → 编译为.card .header .title(权重0-3-0),比.card__title(0-1-0)难覆盖100倍.card上下文,用变量暂存:$block: ".card"; #{$block}__title { color: red; },或者直接写.card__title,别依赖嵌套推导在嵌套块里写@media,Sass默认把它包进当前作用域,结果就是每个断点都复制一遍父选择器链,体积和特异性同步飙升。
.btn { &:hover { @media (max-width: 768px) { color: blue; } } } → 编译为@media (max-width: 768px) { .btn:hover { color: blue; } },看似合理,但若.btn本身在.modal .footer .btn里被嵌套,就会变成.modal .footer .btn:hover,断点规则也跟着膨胀.btn:hover { @at-root (with: media) { @media (max-width: 768px) { color: blue; } } } → @media提至顶层,选择器保持为.btn:hover,不额外加权@at-root (with: media)不能嵌套在@media块内部,否则报错;也不能用screen或query代替media
最常被忽略的一点:嵌套本身不产生性能问题,但&、@extend、@media三者叠加时,会互相放大副作用——比如在@media嵌套里用@extend,再配上带空格的&,编译出的选择器可能既长又重还不可预测。每次保存.scss后,务必用VS Code按住Ctrl点击编译后的CSS,亲眼确认它到底长什么样,而不是靠缩进猜。