@each是唯一可靠方式,因map为键值对结构,@for/@while仅支持数值范围;必须用@each $key, $value in $map解构断点名与宽度等元数据,否则类名缺失关键样式。

SCSS map遍历生成栅格类名时,@each 是唯一可靠方式
直接用 @for 或 @while 无法遍历 Map —— 它们只支持数值范围。SCSS 的 Map 是键值对结构,必须用 @each $key, $value in $map 才能安全取到列数、断点名、宽度百分比等信息。
常见错误是把响应式断点写成数组(如 (sm, md, lg)),结果想用 @each $bp in $breakpoints 去套用栅格规则,但没绑定实际宽度值,生成的 .col-sm-6 类里连 flex-basis 都没有。
- Map 必须包含完整元数据:断点名(
sm)、最小宽度(576px)、栅格列数(12)、各列宽计算逻辑(如calc(100% / 12 * $i)) - 推荐结构:
$grid-breakpoints: (sm: 576px, md: 768px, lg: 992px, xl: 1200px);和$grid-columns: 12;分开定义,避免 Map 嵌套过深 - 若需为每个断点生成
.col-{bp}-{n}(如.col-md-4),外层遍历断点,内层用@for $i from 1 through $grid-columns生成列数
生成 .col-{bp}-{n} 类时,calc() 宽度要防除零和小数精度问题
用 100% / $grid-columns * $i 算宽度看似合理,但 SCSS 编译时若 $grid-columns 是变量而非字面量,可能触发浮点误差(如 calc(100% / 12 * 5) 编译成 calc(100% * 0.4166666667)),某些旧版 Safari 会渲染异常。
更稳妥的是用整数比例字符串拼接,让浏览器运行时计算:
@each $breakpoint, $width in $grid-breakpoints {
@media (min-width: $width) {
@for $i from 1 through $grid-columns {
.col-#{$breakpoint}-#{$i} {
flex: 0 0 calc(100% / #{$grid-columns} * #{$i});
max-width: calc(100% / #{$grid-columns} * #{$i});
}
}
}
}
- 务必用
#{$grid-columns}而非$grid-columns直接参与 calc 字符串拼接,否则 SCSS 会提前计算并转成小数 - 不要写
width: percentage($i / $grid-columns)——percentage()对分数处理不稳定,且不支持响应式上下文 - 如果项目需支持 IE11,
flex-basis不能依赖calc(),得 fallback 到width+box-sizing: border-box
避免在 @each 中重复写 @media 导致 CSS 体积爆炸
每轮 @each 都包裹一层 @media,最终 CSS 会按断点 × 列数 × 属性数线性膨胀。比如 4 个断点 × 12 列 × 2 条样式 = 至少 96 行,还没算 offset/push/pull 类。
真正可控的做法是分层生成:先统一输出基础 .col-*(无断点),再用 @each 单独追加断点专属类(如 .col-sm-*),并用 @at-root 提升作用域:
@at-root {
@each $breakpoint, $width in $grid-breakpoints {
@media (min-width: $width) {
@for $i from 1 through $grid-columns {
.col-#{$breakpoint}-#{$i} {
flex: 0 0 calc(100% / #{$grid-columns} * #{$i});
}
}
}
}
}
-
@at-root防止嵌套层级过深导致选择器冗余(例如不会生成.parent @media... .col-sm-6) - 基础栅格类(
.col-1到.col-12)建议始终存在,断点类仅覆盖响应行为,降低维护成本 - 若用 Dart Sass 1.33+,可配合
@use "sass:math"做整除校验,避免$i > $grid-columns的无效输出
生成 offset/push/pull 类时,margin-left 计算容易漏掉负号或单位
写 .offset-sm-2 时,很多人直接复制 calc(100% / 12 * 2),但 offset 应该是左空两列,即 margin-left: calc(100% / 12 * 2);而 .pull-sm-2(向左拉)实际要负值:margin-left: calc(-100% / 12 * 2)。漏掉负号或写成 left: ... 就完全失效。
- push/pull 类本质是
margin-left位移,不是transform或position,别被 Bootstrap 旧文档误导 - 所有偏移类必须加
!important吗?不用——只要它们的 CSS 优先级高于基础.col-*(比如用更长选择器或后置加载),就能覆盖 - 如果项目用 RTL(如
dir="rtl"),margin-left会自动翻转,无需额外写margin-right版本
最易被忽略的是:Map 键名大小写敏感,sm 和 SM 在同一份样式中混用会导致部分断点类完全不生成,且 Sass 编译器不报错 —— 只静默跳过。检查时盯紧 $grid-breakpoints 的键与类名拼接处是否完全一致。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











