Less 中无原生 for 循环,需用递归 mixin 模拟;定义带计数器的 mixin,每次生成类并自调用,直至满足终止条件(如 @n = 0),否则超默认递归深度(约 100 层)将报错。

Less 里没有真正的 for 循环,但可以用递归 mixin 模拟
Less 本身不支持 for、while 等循环语句,所以“循环生成栅格类”必须靠递归 mixin 实现。核心思路是:定义一个带计数器的 mixin,每次生成一组类(如 .col-1、.col-2),然后调用自身处理下一个数字,直到达到上限。
关键点在于终止条件必须明确,否则编译会报错或无限递归——Less 编译器对递归深度有限制(通常默认 100 层左右),超限会抛出 Recursive call error。
- 递归 mixin 必须有显式的终止分支(比如用
when (@n ) - 计数器应为整数,避免浮点数导致条件判断失效(如
1.999999≠2) - 推荐从大到小递减,便于调试:一眼能看出当前生成的是第几轮
写一个安全的栅格列宽递归 mixin
以生成 .col-1 到 .col-12 为例,每个类对应宽度为 (@n / 12) * 100%。下面这个 mixin 能稳定工作,且兼容 Less 3.5+ 和 4.x:
.make-col-loop(@n, @total: 12) when (@n > 0) {
.col-@{n} {
flex: 0 0 ((@n / @total) * 100%);
max-width: ((@n / @total) * 100%);
}
// 递归调用下一层
.make-col-loop((@n - 1), @total);
}
// 终止:当 @n <= 0 时不匹配,递归停止
.make-col-loop(@n, @total) when (@n <= 0) {}
<p>// 启动:从 12 开始
.make-col-loop(12);注意:@n 是整数变量,@total 是栅格总列数(可复用),((@n / @total) * 100%) 计算时 Less 会自动做浮点运算并保留足够精度。
立即学习“前端免费学习笔记(深入)”;
为什么不用 each() 或 JavaScript 函数?
Less 3.5+ 引入了 each() 函数,但它只接受 list 或 map,且无法在规则块中动态生成选择器名(比如拼接 .col-@{n})。尝试这样写会失败:
each(1px 2px 3px, { .col-@{value} { width: @value; } }); // ❌ 报错:@value 不是合法选择器名更关键的是,Less 的 JS 函数(如 ~`...`)在构建工具链中常被禁用(出于安全或可维护性考虑),而且编译时无法保证运行环境有 Node.js 支持。所以生产项目里,**纯 Less 递归 mixin 是最可靠、最可控的方式**。
-
each()适合遍历已知 list 做样式微调,不适合生成结构化类名 - JS 函数写法会让 CSS 输出失去可预测性,调试困难,CI/CD 构建容易失败
- 递归 mixin 输出完全静态,压缩、source map、lint 都友好
生成响应式栅格时,嵌套递归要小心作用域和命名冲突
如果还要加断点(如 .col-sm-1、.col-md-6),不能简单套两层递归——内层 mixin 会覆盖外层的 @n 变量,导致错乱。正确做法是用不同参数名,并显式传递:
.make-grid-breakpoint(@breakpoint, @max: 12) {
@media (min-width: ~"@{breakpoint}px") {
.make-col-loop-inner(@n, @max) when (@n > 0) {
.col-@{breakpoint}-@{n} {
flex: 0 0 ((@n / @max) * 100%);
}
.make-col-loop-inner((@n - 1), @max);
}
.make-col-loop-inner(@max, @max);
}
}
<p>.make-grid-breakpoint(sm, 576);
.make-grid-breakpoint(md, 768);这里用了 @breakpoint 区分断点名,@n 和 @max 用局部参数隔离。最容易被忽略的是:**嵌套递归的终止条件必须独立判断,不能依赖外层变量状态**——否则某次编译可能漏掉最后几个类,且难以复现。


















