%占位符比普通类名更省体积,因其编译后不输出css规则,且@extend时合并选择器;而普通类名会保留自身规则并可能引入冗余伪类。

直接用 @extend 普通类名(比如 .btn)会导致 CSS 体积不减反增;真正能压缩输出、避免重复的,是 % 占位符 + @extend 的组合,但必须满足三个前提:扁平命名、无嵌套定义、跨文件正确引入。
为什么 %button-base 比 .button-base 更省体积
占位符以 % 开头,编译后完全不输出任何 CSS 规则;而普通类名一旦定义,哪怕没人用,也会原样出现在最终 CSS 里。更关键的是:@extend %button-base 会让所有调用它的选择器合并成一条规则,比如 .primary-btn, .secondary-btn { … };但 @extend .button-base 会把 .button-base 自身那条规则也保留下来,还可能拖入它的伪类(.button-base:hover),造成冗余。
-
%reset-text被.title、.subtitle、.caption同时继承 → 编译后只有一条.title, .subtitle, .caption { margin: 0; line-height: inherit; ... } -
.reset-text被同样三个类继承 → 编译后多出一条无用的.reset-text { ... },且三个类各自带完整声明副本 - 如果
.reset-text本身含&:hover,那.title:hover、.subtitle:hover全都会被生成——这通常不是你想要的
@extend %placeholder 在嵌套结构里容易失效
SCSS 的 @extend 只作用于当前选择器本身,不会穿透嵌套层级。写成 .card { @extend %bordered; &__header { color: red; } },只有 .card 会获得 %bordered 的样式,.card__header 不会自动继承。
- 想让子元素也应用同一套基础样式?得单独写
.card__header { @extend %bordered; } - 不要在
@media块内定义%—— 它会被限制在媒体查询作用域里,外部@extend会报错Extending unknown pseudo-class "%xxx" - 避免深度嵌套定义占位符,比如
.modal .content %base,否则继承时会生成.modal .content .alert这种冗长选择器
跨文件使用 % 占位符必须显式引入
Dart Sass v7.0+ 默认禁止隐式跨文件 @extend,% 占位符不会自动暴露到其他文件。
- 用
@use "base" as *后,可直接写@extend %reset-text - 用
@use "base"(不加as *),则必须写@extend base.%reset-text - 不能循环依赖:A 文件
@use "B",B 文件就不能再@use "A" - 如果编译报错
Warning: You're extending a selector that isn't defined,先检查@use顺序和路径是否正确,而不是加!optional掩盖问题
哪些场景该坚决放弃 %,改用 @mixin
% 是纯静态复用,零灵活性;一旦需要传参、条件分支或包裹 @media,它就无能为力。
- 要给不同组件加不同圆角?
@mixin border-radius($r)可传值,%rounded只能写死border-radius: 4px - 响应式字体大小?
@mixin responsive-font($min, $max)内部可放@media,%text-sm无法做到 - 多个类只需复用其中几个属性(比如只要
display和vertical-align,不要margin)?%是全量继承,此时@include小型原子 mixin 更精准 - 用 BEM 或 CSS Modules?
@extend会强行合并选择器,破坏命名空间隔离,这时@mixin是唯一安全选项
最常被忽略的一点:占位符里混入布局属性(margin、display、position)看似方便,实则让复用变成枷锁——换一个容器就要覆盖这些值,最后满屏 !important。真正可长期复用的,只有排版、颜色、过渡这类与 DOM 结构解耦的原子声明。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











