@extend 的本质是选择器合并,而不是很多人理解的“样式复用”。它会把所有调用位置的选择器直接并入被继承的规则中,因此很容易生成冗长的 CSS 选择器链、在媒体查询中失效,甚至破坏模块隔离。实际开发中,@extend 更适合只用于没有上下文依赖的 %占位符。

@extend 本质是选择器合并,不是样式复用
很多开发者误以为 @extend 是把一段样式“拷贝”到别处,实际上它做的事情是:把所有调用它的选择器,统一塞进被继承的选择器规则中。比如你定义了 %flex-center,然后在 .card-header、.modal-footer、.toast-body 三处使用 @extend %flex-center,Sass 编译后会输出:.card-header, .modal-footer, .toast-body { display: flex; justify-content: center; align-items: center; }。这种场景通常没有问题。但如果 %flex-center 被定义在嵌套结构中,例如 .layout { %flex-center { ... } },那么 Sass 会忠实地把父级路径一并拼接进去,最终生成类似 .layout .card-header, .layout .modal-footer 这样带有冗余上下文的 CSS 选择器。
嵌套、媒体查询、伪类会让选择器爆炸式增长
浏览器在匹配 CSS 选择器时采用从右向左的方式,像 .a .b .c .d .e 这样的长链选择器,不仅会让 CSS 文件体积增大,也会增加解析负担,旧版 Safari 和 IE 甚至还存在选择器长度限制。以下几种写法尤其容易让 Sass 的 @extend 生成大量 CSS 选择器:
- 在嵌套规则中使用
@extend:例如.card { .header { @extend %text-sm; } }→ 会输出大量带有.card前缀的选择器变体 - 跨媒体查询调用:
@media (min-width: 768px) { .btn { @extend .base-btn; } }→ Sass 通常只能把.base-btn的相关声明复制进媒体查询块中,造成重复输出 - 继承目标包含伪类:
%btn-hover { &:hover { color: blue; } }被多处@extend→ 所有继承者都会被附带上:hover,而且无法单独移除 - 基类本身是复合选择器:
.utility .text-center被@extend→ 输出结果往往是.card .utility .text-center,而不是很多人预期的.card .text-center
模块隔离失效时,@extend 会静默失控
从 Dart Sass 7.0+ 开始,默认采用模块隔离机制:@use 表示显式声明依赖,而 @import 更接近于文本级别的直接拼接。问题通常就出在这一点——如果 %placeholder 定义在 A 文件中,B 文件既没有通过 @use 引入,也没有通过 @import 拼接进来,却直接调用 @extend,那么 Sass 很可能出现静默失效,或者路径解析异常。最终结果就是:构建过程中某些扩展被悄悄忽略,开发者却误以为是 @extend 本身没写对,于是继续不断补规则,最后不仅没解决问题,反而让整个依赖关系变得更混乱。
判断 @extend 是否已经“失控”,不能只看 SCSS 源码表面是否简洁,更关键的是查看编译后的 CSS 输出,以及浏览器 DevTools 中选择器的真实生效路径。可以先执行 sass --style=compressed src/main.scss dist/main.css,打开生成后的 CSS 文件,直接搜索 , (逗号),观察是否已经出现超长的选择器合并链;然后再到浏览器中选中对应元素,沿着样式来源逐步回溯,确认这条规则到底来自哪一行、哪个文件。
真正可控的替代方案不是不用,而是换锚点
@extend 并不是完全不能用,问题在于继承目标是否选对。核心区别在于:%placeholder 不会直接输出任何 CSS,只作为继承锚点存在;而 @extend .btn 则会把 .btn 这个真实类选择器一起拖进来,同时带入它的语义和潜在上下文污染。
- 只对“完全静态、没有上下文依赖”的纯功能类使用
%placeholder + @extend,例如%sr-only、%clearfix - 避免将带状态(
.btn:hover)、响应式(.md:flex)、父级依赖(.card > .title)的规则作为继承目标 - 改用
@mixin封装可配置逻辑,例如@mixin flex-center($direction: row),这样既能避免选择器合并,也更方便局部覆盖 - 工具类优先采用 HTML 多类组合方式,比如
,而不是依赖 Sass 层的继承机制
真正复杂的地方不在语法本身,而在继承目标的语义边界控制——一个 %flex-center 一旦放错位置,就可能让多个组件的 CSS 同时带上原本不该出现的父级路径,最终影响整个样式体系的可维护性和搜索引擎更偏好的页面性能表现。
