@extend会生成超长选择器链,因其将调用处与被继承选择器的祖先路径拼接,导致冗余、性能下降及浏览器限制问题。
@extend不是复制样式,而是把所有调用它的地方“合并进”被继承的选择器规则里。比如%flex-center被.card、.modal、.btn-group三处@extend,编译后就是.card, .modal, .btn-group { display: flex; }——这本身没问题;但一旦%flex-center定义在.layout .sidebar嵌套块内,Sass就会忠实拼出.layout .sidebar .card, .layout .sidebar .modal这种带冗余祖先路径的选择器。
浏览器从右往左匹配 CSS,.a .b .c .d .e这类长链显著拖慢解析速度,旧版 Safari 和 IE 甚至可能触发 4095 字符限制。更隐蔽的问题是:多个组件在@media或&:hover里调用同一占位符,Sass 会把断点和伪类也拼进去,产出.btn:hover, .modal .btn:hover, .page .modal .btn:hover这种爆炸式组合。
_base.scss里定义的%flex-center被_layout.scss中.page .section .card调用时拉入整条父链.utility .text-center,否则继承者会得到.card .utility .text-center而非预期的.card .text-center
不是所有@extend都危险,但以下模式几乎一定失控:
.card { .header { @extend %text-sm; } } → 可能生成.card .header, .card .title, .alert .header等一堆带.card前缀的变体%btn-hover { &:hover { color: blue; } }被多处@extend,结果所有按钮都强制获得 hover 样式,且无法单独关闭.clearfix)而非%clearfix作继承目标:既输出无用的.clearfix类,又让所有@extend它的地方被卷入同一规则块display、margin、position等布局属性,换场景必须用!important覆盖,违背继承初衷%占位符才是安全路径——它不输出 CSS,只在@extend时内联合并声明。但它必须守规矩,否则调试崩溃、编译失败、线上漏样式:
%开头声明,例如%sr-only、%clearfix;写成.sr-only或sr-only都无效@extend调用,否则编译后彻底消失——你查不到它,也 debug 不到它.card { .title { @extend %heading; } }是危险操作,Sass 会尝试拼接.card .title和%heading的全部上下文路径margin、display、float等强耦合布局属性@extend合并选择器,@mixin复制声明——二者根本不是替代关系,选谁取决于你是否需要「样式归属权转移」:
.card__title和.list__item-title的选择器合并,破坏命名空间@mixin responsive-text更可控,每个调用点都是独立规则@mixin spacing($size: 1rem)
font-weight: 600)重复出现 37 次;而@mixin复制的是声明本身,cssnano 能真正合并它们真正难处理的从来不是语法怎么写,而是当%flex-center被 12 个不同模块的组件在 3 层嵌套里调用时,你得立刻判断:这次该删掉它,还是改用@mixin,或者干脆 HTML 上加多一个 class。