Less中可用递归混合模拟循环生成栅格类,如.make-grid(12)配合守卫when (@i > 0)和插值@{i}生成.col-1至.col-12,避免硬编码;each()和JS函数因兼容性、静态分析及构建工具限制不推荐。

Less 中用 .loop() 递归混合生成栅格类名
Less 本身不支持 for 循环语法,但可通过递归混合(recursive mixin)模拟循环。这是生成 .col-1 到 .col-12 这类栅格类最可靠的方式,避免硬编码。
关键点在于:定义一个带计数器参数的混合,每次调用时递减,直到终止条件满足。注意必须显式写 when (condition) 守卫,否则会无限递归报错。
-
.col-@{i}类名中使用插值@{i},不能直接写.col-i - 起始调用必须带初始值,比如
.make-grid(12);,否则不会触发任何输出 - 守卫条件要覆盖边界,例如
when (@i > 0)比when (@i >= 1)更安全
.make-grid(@i) when (@i > 0) {
.col-@{i} { width: (100% / 12 * @i); }
.make-grid(@i - 1);
}
.make-grid(12);为什么不用 each() 或 JavaScript 函数?
Less 3.5+ 引入了 each(),但它要求传入 list 或 map,且无法在顶层 CSS 规则中直接展开为多个选择器——它更适合批量设置属性值,而非生成类名结构。而 JS 函数(如 ~`Array.from({length:12},(_,i)=>`.col-${i+1}`)`)属于运行时求值,破坏 Less 的静态编译特性,导致 source map 错乱、IDE 无法跳转、构建工具(如 webpack-less-loader)可能报错。
-
each()只能用于属性值批量赋值,例如each(@breakpoints, { ... }) - JS 模板字符串在 Less 中是反引号
~`...`,但编译期不可控,调试困难 - 递归混合是官方推荐、全版本兼容、可静态分析的方案
响应式栅格需要嵌套循环?别嵌套,用多层递归混合
为不同断点(sm、md、lg)生成 .col-sm-4、.col-md-6 等,不是在单个混合里加两层循环,而是拆成「断点循环 + 列数循环」两个正交混合。这样逻辑清晰,也方便单独覆盖某一层。
立即学习“前端免费学习笔记(深入)”;
- 先定义
.make-grid-breakpoint(@bp, @max: 12),内部调用.make-col(@bp, @i) - 每个断点调用一次,比如
.make-grid-breakpoint(sm, 12);、.make-grid-breakpoint(md, 12); - 避免在
.make-col内部再调用另一个递归混合,否则编译速度下降明显,且容易触发栈溢出警告
.make-grid-breakpoint(@bp, @max) when (@max > 0) {
.make-col(@bp, @max);
.make-grid-breakpoint(@bp, @max - 1);
}
.make-col(@bp, @i) {
.col-@{bp}-@{i} { width: (100% / 12 * @i); }
}编译后 CSS 体积和选择器性能要注意什么
生成 12 列 × 4 断点 = 48 个类,看起来不多,但若再叠加 offset/push/pull 变体,很快突破 200 行。这些类全部进入 CSS 文件,即使没被 HTML 使用,也会增加网络传输和浏览器解析负担。
- 实际项目建议按需生成,比如只导出
@grid-columns: 12和@grid-breakpoints: sm md lg两个变量控制范围 - 不要为所有断点都生成全套列宽,移动端常只需
col-6、col-12等少数几个 - 选择器层级越深、类名越多,CSSOM 构建时间越长;纯类名(如
.col-6)比属性选择器快,但数量失控仍影响渲染
真正麻烦的是维护:一旦设计稿改成 16 栅格,所有递归混合、守卫条件、计算公式都要同步改,而且没法靠 IDE 自动重命名——得靠人眼核对每一处 @i 和 12 是否一致。


















