占位符选择器%是仅用于@extend的静默模板,不输出css;而普通类(如.btn)一旦定义就会原样输出,造成冗余。%只在被@extend调用时才将样式注入目标选择器,适合抽象不可直接使用的样式层。

占位符选择器 % 是什么,它和普通类有什么区别?
占位符选择器 % 本身不会输出任何 CSS,只有被 @extend 引用时,才会把样式合并进目标选择器里。它不是类(.btn),也不是 ID(#header),不能直接写在 HTML 中用——写了也没效果。
常见错误现象:%clearfix 写在 HTML 的 class 里,结果清浮动失效;或者误以为 %base-text 能像 .base-text 那样独立复用,结果编译后完全没生成对应 CSS。
-
%只用于抽象共用样式逻辑,不参与最终 HTML 结构 - 它比
mixins更省代码体积:多个@extend %xxx会合并成一条规则,而@include每次都复制整段声明 - 不支持嵌套调用:
@extend %inner不能放在另一个%里(Sass 会报错"Extends may only be used within rulesets.")
@extend %xxx 为什么会合并选择器而不是重复输出?
Sass 在编译阶段做“选择器合并优化”:当多个选择器都 @extend %reset,它会把它们收集起来,生成一条类似 h1, p, .article-content { margin: 0; padding: 0; } 的规则,而不是给每个地方单独写一遍重置样式。
但这个行为有前提:所有被 extend 的目标必须是简单选择器(如 .title、button),一旦出现复杂组合(如 .card .title 或 .btn:hover),Sass 就无法安全合并,会退化为重复输出——这是最常被忽略的坑。
- ✅ 安全合并:
.header,.footer,h1同时@extend %no-margin - ❌ 不合并:
.sidebar .title和.main .title都@extend %bold→ 编译后各自生成独立规则 - ⚠️ 注意:伪类、属性选择器、后代/子代关系都会破坏合并能力
什么时候该用 % 而不是 @mixin?
核心判断依据是“是否需要参数”和“是否追求极致压缩”。% 不能传参,@mixin 可以;% 编译后去重,@mixin 复制粘贴。
典型场景对比:
- 颜色主题基底(固定值)→ 用
%theme-base:比如统一color、background、border组合,多处@extend后只出一次 - 响应式断点包装(需传入
$breakpoint)→ 必须用@mixin respond-to,%做不到 - 按钮基础边框/圆角/光标 →
%btn-base很合适;但不同尺寸的padding和font-size就得靠 mixin 参数控制
性能影响:项目中大量使用 @extend 且选择器复杂度高时,Sass 编译速度会明显下降——因为要反复分析选择器可合并性。
为什么 % 有时会意外污染全局选择器?
问题出在 @extend 的“选择器提升”行为:如果 %flex-center 定义了 display: flex 等,而你在 .modal .content 里 @extend %flex-center,Sass 有可能把 .modal .content 提升到顶层,甚至和其它无关选择器合并,导致样式作用范围失控。
这不是 bug,是设计使然——Sass 优先保证语义合并,而非作用域隔离。
- 避免方式:用更具体的选择器命名,比如
%_flex-center(加下划线暗示私有),或限定作用域:%utility-flex-center - 检查方法:开启 Sass 的
--style expanded编译,看输出 CSS 是否出现意料外的长选择器链 - 终极保险:对关键 UI 组件(如
.card、.form-field),宁可用@include+ BEM 命名,也不用%冒险共享
真正难的不是写 %,而是预判它在不同上下文里会被怎么合并——尤其跨文件 @import 后,依赖关系变隐晦,稍不注意就让某个 h2 突然继承了本不该有的 text-transform: uppercase。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











