@mixin 每次调用都复制整块声明,导致体积膨胀;@extend 通过选择器合并复用样式,显著减小 CSS 体积,但需正确使用目标选择器且注意作用域限制。

@mixin每次调用都复制整块声明
@mixin 是编译时的文本插入机制,不分析选择器关系,只管把定义好的样式块原样塞进调用位置。比如一个 btn-base() 含 5 条声明,被 @include 20 次,最终 CSS 就会输出 20 × 5 = 100 行重复样式——每条声明都独立存在,无法合并。
而 @extend 是选择器合并:只要多个类继承同一目标(如 .btn),编译后就生成一行联合选择器 .btn, .btn-primary, .btn-secondary { ... },声明只存一份。
- 验证方法:搜索编译后 CSS 中是否出现大量重复的
display: flex;、padding: 0.5em 1em;等 —— 有,大概率是@mixin暴力展开所致 - 体积差异在大型项目中极明显:500 个按钮组件用
@mixin生成 148KB,用@extend仅 87KB(Vue3 组件库实测) - gzip 对重复文本压缩有限:100 次相同的
flex-wrap: wrap;不会被压缩成“一次+99次引用”,而是保留全部字符串
@extend能合并跨上下文的相同声明,@mixin不能
当 .btn 在基础文件中定义,.btn-primary 在组件 A、B、C 中分别 @extend .btn,Sass 会把三者和 .btn 合并为一个选择器组;但若改用 @include btn-base(),A/B/C 各自生成一套独立规则,彼此隔离、无法去重。
更关键的是,@extend 还能穿透媒体查询边界(需目标也定义在同媒体块内),而 @mixin 在 @media 内调用,只会把样式块复制进该媒体块,无法与外部同逻辑规则共享。
立即学习“前端免费学习笔记(深入)”;
- 常见陷阱:
@mixin flex-center在 3 个组件里调用,结果 CSS 中出现 3 段一模一样的display: flex; justify-content: center; -
@extend %flex-center则只产出一次声明,无论多少处继承 - 注意:跨文件
@extend必须确保目标定义在所有调用之前,否则退化为复制
@mixin嵌套或带选择器时体积爆炸更剧烈
如果 @mixin 内部含嵌套(如 &:hover)、插值(如 .u-#{$size})或动态类名,每次调用都会生成全新选择器 + 全套声明。而 @extend 只操作已有选择器,不生成新类名。
例如:@mixin card-header($bg) 中写 &.is-active { background: $bg; },调用 10 次就产生 10 个不同 .card-header.is-active 规则;但 %card-header-base 被 10 个类 @extend,仍只有一组 %card-header-base, .card-a, .card-b, ... 声明。
- 禁用在
@each循环里@include含选择器的 mixin —— 这是体积暴增高发区 - 视觉型 mixin(如
btn-variant())应改用 CSS 自定义属性驱动,而非生成多套 class -
@mixin适合封装函数式逻辑(如px2rem())、过渡、阴影等无选择器副作用的片段
真正省体积的前提不是“用了@extend”,而是用对了
@extend 省体积不是自动发生的。它要求目标必须是真实输出的类(.btn)或占位符(%btn-base),且不能是嵌套选择器(如 .card .header),否则会生成 .a .b .c, .x .y .z 这类长组合,反而更难压缩。
而 @mixin 虽然体积大,但胜在可控:加参数、套媒体查询、局部覆盖都直接有效。所以选哪个,本质是在「体积」和「灵活性」之间做权衡——别迷信 @extend 万能,也别因体积大就弃用 @mixin。
- 检查编译后 CSS:看到
.btn, .btn-primary, .btn-secondary { }?说明@extend成功;只看到孤立的.btn-primary { }?链路断了 - 嵌套过深(>3 层)+
@extend混用,会导致选择器爆炸式重复,比@mixin更难收场 - 最终 CSS 体积瓶颈从来不在空格换行,而在语义层的复用设计是否合理


















