@extend不是减少冗余的工具,而是制造冗余的常见源头;它本质是选择器合并而非样式复用,易致CSS体积膨胀、选择器爆炸及权重失控。

@extend 不是减少冗余的工具,而是制造冗余的常见源头;它本质是选择器合并,不是样式复用——用错就必然导致 CSS 体积膨胀、选择器爆炸、权重失控。
为什么 @extend 会让编译后 CSS 更大
@extend 把所有调用它的选择器“塞进”被继承目标的规则块里,而不是提取公共声明。一旦多个地方继承同一占位符,Sass 就生成超长逗号分隔列表,比如 .btn, .btn-primary, .btn-secondary, .card-header, .modal-footer { … }。这不是 bug,是设计行为。
- 若
%flex-center定义在.layout .sidebar嵌套块内,.card {@extend %flex-center}会产出.layout .sidebar .card这种带冗余祖先路径的选择器 - 跨文件引用未显式
@use的%placeholder,Dart Sass v7.0+ 默认静默失败或拼错路径,构建时漏样式,开发者常误加更多@extend补救,形成恶性循环 - 继承目标含
&:hover或@media,Sass 会把条件状态也拼进去,产出.btn:hover, .card .btn:hover, .page .card .btn:hover这类组合,浏览器匹配极慢
哪些 @extend 写法几乎一定失控
以下模式在真实项目中反复引发线上问题,应直接禁用:
-
.card { .header { @extend %text-sm; } }→ 编译后可能生成.card .header, .card .title, .alert .header等一堆带.card前缀的变体 - 用具名类(如
.clearfix)而非%clearfix作继承目标:既输出无用的.clearfix类,又让所有@extend它的地方被卷入同一规则块 -
%btn-hover { &:hover { color: blue; } }被多处@extend→ 所有按钮强制获得 hover 样式,且无法单独关闭 - 在
@media块内定义%base,外部文件@extend %base直接报错Extending unknown pseudo-class "%base"
如何安全地用 % 占位符替代 @extend
% 占位符本身不输出 CSS,只作为继承锚点,但必须守三条铁律,否则调试崩溃、线上漏样式:
立即学习“前端免费学习笔记(深入)”;
- 必须以
%开头声明,例如%sr-only、%clearfix;写成.sr-only或sr-only都无效 - 必须被至少一个
@extend调用,否则编译后彻底消失——你查不到它,也 debug 不到它 - 不能出现在嵌套规则内部:
.card { .title { @extend %heading; } }是危险操作,Sass 会尝试拼接全部上下文路径,产出类似.card .card .title的冗余选择器
跨文件使用必须显式 @use,例如 @use "base" as base 后写 @extend base.%reset-text;不能循环依赖,A 文件 @use "B",B 就不能再 @use "A"。
什么时候该换用 @mixin 而不是 @extend
@mixin 是文本插入,不碰选择器关系,天然规避爆炸风险,适合绝大多数复用场景:
- 需要传参:如
@mixin btn($color)、@mixin spacing($size) - 含媒体查询或伪类:
@mixin responsive-grid里可自由写@media和&:hover - 需动态计算:
lighten($color, 10%)、rem-calc(16px)等函数只能在@mixin中生效 - 组件变体(如
.btn--large)建议用@mixin或直接写类,而非靠@extend微调
真正该用 @extend 的只剩一种情况:语义明确、永不单独存在、无参、无嵌套、不含状态的原子功能类,比如 %visually-hidden、%reset-list。其余全是 @mixin 或 CSS 自定义属性的事。


















