@each 遍历 Sass Map 生成状态类最直接,需确保 key 为合法 CSS 标识符、用嵌套 Map 管理多属性、配合 @at-root 输出顶层类名,并统一命名空间避免冲突。

用 @each 遍历 Sass Map 生成状态类最直接
只要 Map 结构清晰,@each 就是唯一需要的工具。别去套循环函数或递归 mixin —— Sass 原生遍历足够干净,也避免隐式作用域污染。
常见错误是把状态名硬编码进选择器拼接里,比如写成 .btn-#{$state} 却忘了 $state 可能含空格或特殊字符;正确做法是确保 Map 的 key 是合法 CSS 标识符(如 primary、disabled),value 是对应样式声明块或颜色值。
- Map 定义推荐用嵌套结构:
$states: (hover: #0066cc, active: #004488, disabled: #cccccc); - 遍历时用两个变量接收 key 和 value:
@each $state, $color in $states { ... } - 选择器拼接必须用
#{$state}插值,不能写死字符串
处理多属性状态类要用嵌套 Map + @each 套用
单值 Map(如只存颜色)适合简单场景;真实项目中状态往往带多个样式规则(颜色、背景、光标、指针事件),这时必须用嵌套 Map,否则逻辑会迅速失控。
例如 disabled 状态通常要同时改 color、background-color、cursor 和 pointer-events —— 如果还用扁平 Map,就得为每个属性建一个 Map,维护成本翻倍。
立即学习“前端免费学习笔记(深入)”;
- 嵌套写法示例:
$states: (hover: (color: #0066cc, transform: translateY(-1px)), disabled: (color: #999, background-color: #f5f5f5, cursor: not-allowed)); - 外层
@each $state, $props in $states,内层再@each $prop, $val in $props - 注意:嵌套 Map 的 value 必须是 map 类型,空状态(如
focus没额外样式)可设为null或空 map(),避免@each报错
@at-root 是防止嵌套污染的关键
如果在某个选择器块内(比如 .btn { ... })直接写 @each,生成的状态类会变成 .btn:hover 这种后代/伪类形式,而不是独立的 .btn-hover 类 —— 这通常不是你想要的。
@at-root 能强制跳出当前嵌套上下文,让生成的类名保持顶层作用域。不加它,就等于白写。
- 错误写法:
.btn { @each $s, $v in $states { .btn-#{$s} { color: $v; } } }→ 输出.btn .btn-hover { ... } - 正确写法:
.btn { ... @at-root .btn-#{$s} { color: $v; } }→ 输出.btn-hover { ... } - 如果整个状态类体系都独立存在(不依附于基础组件),
@at-root应放在@each外层,而非选择器内部
生成类名时别忽略 BEM 或命名空间冲突
自动输出的类名很容易和项目已有命名约定打架。比如 .disabled 可能和框架的 .is-disabled 冲突,或和 JS 添加的 .active 类行为不一致。
这不是 Sass 能解决的问题,但必须在写 Map 前想清楚:状态类是纯视觉修饰(应带前缀),还是语义化状态(需对齐 JS 行为)?
- 推荐统一加命名空间:
btn--hover、btn--disabled(BEM modifier)或u-disabled(实用工具类) - Map 的 key 可以是短名(
hover),但拼接时显式加上分隔符:.btn--#{$state} - 若项目已用
data-state属性控制样式,就别生成类 —— 直接输出[data-state="#{$state}"]更合理
Map 遍历本身很简单,难的是定义清楚状态边界、命名意图和与 JS 的协作方式。生成出来的类名一旦上线,改起来比逻辑还疼。


















