用 $spacers map 配 @each 生成边距工具类,键为语义名、值为带单位长度;margin/padding 应分层 map 管理;响应式按需生成,方向控制用嵌套 $sides map,单位须统一且注明约束。
直接说结论:用 $spacers 变量配 @each 循环,比手写 .m-1 .m-2 … 省事且可维护。关键不是“能不能”,而是变量结构设计是否支持响应式扩展和语义分组。
常见错误是把 $spacers 写成纯数字数组(如 (4, 8, 12, 16)),后续加 rem 单位或断点适配就卡住。正确做法是用 map,键为语义名,值为带单位的长度:
$spacers: ( "0": 0, "1": 0.25rem, "2": 0.5rem, "3": 1rem, "4": 1.5rem, "5": 3rem);
"3")会变成类名后缀,别用数字开头以外的字符,否则生成的 CSS 类名不合法rem、px 或 em),纯数字会被 SCSS 当成无单位数值,编译时报 Invalid null operation
@each $breakpoint, $values in $grid-breakpoints 即可,但先确保基础 map 结构立得住因为 margin 和 padding 的作用域完全不同:前者影响布局流与兄弟元素间距,后者只影响自身盒内空间。混在一起生成(比如 .p-2 和 .m-2 共用同一组值)看似省代码,实际会提高认知成本——改一个值就得同步确认两边语义是否真一致。
实操建议用两层 map 区分:
立即学习“前端免费学习笔记(深入)”;
$spacers: ( "margin": ( "1": 0.25rem, "2": 0.5rem ), "padding": ( "1": 0.25rem, "2": 0.5rem ));
map-get($spacers, "margin") 显式取值,避免隐式 fallback 导致意外交互"negative" 分组专供负边距(如 .m-n2),这时必须单独建 map,不能靠运算符动态加负号——SCSS 不支持在 map key 上做表达式计算padding 跟 margin 共享变量,它们的典型使用场景本来就不重合:padding 常用于卡片内边距,margin 多用于组件间隔离盲目给每个间距加 sm:、md:、lg: 前缀,会导致最终 CSS 多出 3 倍规则。真正可控的做法是:只对高频变化的断点+高频使用的间距组合生成响应式类。
例如,只在 md 断点以上启用 "4" 和 "5" 间距,其余保持默认:
@each $prop, $value-map in $spacers { @each $size, $length in $value-map { .#{$prop}-#{$size} { #{$prop}: $length; } @if $size == "4" or $size == "5" { @include media-breakpoint-up(md) { .#{$prop}-md-#{$size} { #{$prop}: $length; } } } }}
@include media-breakpoint-up(md) 必须来自 Bootstrap 或自定义 mixin,别直接写 @media (min-width: 768px) —— 后续换断点阈值时要全局搜替换$size == "4" 而不是 index($value-map, $length) > 3,后者在 map 中不可靠,SCSS 的 map 没有稳定索引顺序.m-md-4 比 .m-4-md 更符合主流 CSS 方法论(属性-断点-值),也方便 IDE 自动补全方向控制不是靠多写几个循环,而是提前把方向映射关系固化进变量。硬编码 "t": "margin-top" 这种写法后期难维护,应该用嵌套 map 描述维度与属性的对应:
$sides: ( "t": "margin-top", "b": "margin-bottom", "l": "margin-left", "r": "margin-right", "x": ("margin-left", "margin-right"), "y": ("margin-top", "margin-bottom"));
然后在主循环里展开:
@each $side-key, $prop-name in $sides { @each $size, $length in map-get($spacers, "margin") { .m#{$side-key}-#{$size} { @if type-of($prop-name) == "list" { @each $prop in $prop-name { #{$prop}: $length; } } @else { #{$prop-name}: $length; } } }}
type-of($prop-name) == "list" 是判断 "x" 和 "y" 的唯一可靠方式,别用 length($prop-name) > 1 —— 字符串也返回长度,会误判m + 方向码(如 m-t-2)比 mt-2 更易扩展:将来加 padding 只需换首字母 p-t-2,不用改命名逻辑auto 特例:对 "m-x-auto" 这种需求,得单独写分支,不能指望循环自动覆盖最常被忽略的是单位一致性——rem 值在不同根字体大小下表现差异极大,如果项目用了 clamp() 动态根字号,所有 rem 边距都会偏移,这时就得切回 em 或视口单位,并在变量注释里标清楚约束条件。