SCSS中可用&:#{$state}结合@each批量生成独立伪类规则,如.btn:hover、.btn:focus等;需用map映射不同状态样式,并严格按LVHA顺序书写以确保层叠正确。

伪类状态太多,手写重复代码太累
SCSS 的 @each 本身不能直接生成伪类选择器(比如 :hover、:focus),因为伪类不是属性值,而是选择器结构的一部分。强行用 @each 遍历字符串列表拼接选择器,容易产出无效 CSS(如 .btn:hover:focus 这种错误连写),或破坏嵌套上下文。真正能批量定义伪类状态的,是结合 & 父选择器引用 + @each 遍历状态名,让 SCSS 编译器正确解析为独立规则。
用 & + @each 正确生成多个伪类规则
核心是把状态名当变量,用 <state></state> 形式拼进选择器,SCSS 会自动将 & 替换为外层选择器。必须确保每个状态都作为独立伪类出现,而不是叠加。
.btn {
padding: 8px 16px;
border: 1px solid #ccc;
@each $state in hover, focus, active, disabled {
&:#{$state} {
opacity: 0.7;
outline: none;
cursor: default;
}
}
}
编译后生成的是四个独立规则:
.btn:hover { ... }
.btn:focus { ... }
.btn:active { ... }
.btn:disabled { ... }
- 注意:
$state是字符串,必须用#{$state}插值,不能直接写&:$state -
disabled是属性,但:disabled是伪类,浏览器支持良好;若需兼容老版本 IE,慎用:disabled伪类,改用.btn.disabled类控制 - 不推荐在同一个
@each块里混用伪类和类名(如hover, .loading),会导致选择器结构错乱
需要差异化样式时,配状态映射表更可控
如果不同状态要应用不同样式(比如 :hover 改背景,:focus 加边框,:disabled 灰掉文字),硬塞进一个 @each 块里会难维护。用 map 存映射关系,再用 @each $state, $props in $state-map 拆解:
立即学习“前端免费学习笔记(深入)”;
$state-map: (
hover: (background-color: #007bff),
focus: (outline: 2px solid #007bff),
disabled: (color: #999; cursor: not-allowed)
);
.btn {
@each $state, $props in $state-map {
&:#{$state} {
@each $prop, $value in $props {
#{$prop}: $value;
}
}
}
}
- map 的 value 必须是括号包裹的 list 或 map,否则
@each解构失败 - 嵌套
@each中的#{$prop}插值是安全的,SCSS 支持动态属性名 - 这种写法牺牲一点编译速度,换来明确的状态-样式绑定,适合组件库级维护
别忘了伪类顺序:LVHA 规则依然生效
即使用了 @each,生成的 CSS 顺序仍影响层叠。:link → :visited → :hover → :active(LVHA)必须遵守,否则 :hover 可能被 :visited 覆盖。SCSS 不会自动排序,得靠你写 @each 时按顺序列状态:
@each $state in link, visited, hover, active { ... }
- 实际项目中,
:link和:visited通常只用于a标签,按钮类一般不用,所以常见顺序是hover, focus, active, disabled - 如果同时写了
&:focus和&:focus-visible,后者应放在后面,确保可访问性优先级更高 - 编译后检查生成 CSS 的顺序,别依赖 SCSS 源码顺序 —— 某些构建工具(如旧版 dart-sass)可能重排
:disabled 在某些场景下语义不如 [disabled] 属性选择器可靠)。


















