less不支持运行时递归,其“递归循环”实为mixin编译期自我调用,须设终止条件(如计数器递减至0)、传参更新状态,并配置--max-call-levels;否则报错。

Less里没有真正的递归函数,只有Mixin的自我调用
Less不支持运行时计算或函数式递归,.loop()这类写法会报错:Recursive call to '.loop'(除非显式启用递归限制)。所谓“递归循环”,本质是Mixin通过自身调用+条件终止模拟循环,属于编译期展开,不是运行时逻辑。
关键点在于:必须提供明确的终止条件(如计数器递减到0),否则Less编译器会无限展开并报错。
- 必须用参数传递状态(如
@i计数器),不能依赖全局变量 - 每次调用需修改参数值(如
@i: (@i - 1)),否则无法收敛 - Less 4.0+ 默认禁用无限制递归,需在编译时加
--max-call-levels=100(或更高)才允许深度嵌套
实现垂直栅格布局的Loop Mixin示例
常见需求:生成12列栅格的.col-1到.col-12类。用Mixin自我调用比手写12行更可靠,也便于后期调整列数。
.make-cols(@n, @i: 1) when (@i =// 启动:生成12列 .make-cols(12);
注意:when (@i = 是守卫条件(guard),决定是否继续展开;缺它就会无限递归。Less会把上面代码编译成12个独立CSS规则,不保留任何逻辑痕迹。
容易踩的坑:参数未更新、守卫写反、编译器限制
实际写的时候,这三个错误占90%以上:
- 忘记更新参数:
.loop(@i)里没写.loop((@i - 1)),导致守卫永远为真 → 编译卡死或报Maximum call stack size exceeded - 守卫条件写反:
when (@i > 0)却用@i: (@i + 1)递增 → 永远不满足终止条件 - 未配编译参数:Webpack less-loader默认
maxCallStackDepth为10,生成20项就会截断,需显式设为50或更高
调试技巧:临时把Mixin改成.debug(@i) { content: "@{i}"; },看输出是否按预期递增/递减。
替代方案:用JavaScript插件或转用PostCSS
如果循环逻辑复杂(比如需要判断素数、生成斐波那契宽度),Less就力不从心了。此时更务实的做法是:
- 用
less-plugin-functions引入JS函数,在.less里调用myLoop(12) - 直接切到PostCSS +
postcss-for,语法更接近真实for循环:@for $i from 1 to 12 { .col-$i { width: calc(100% / 12 * $i); } } - 静态内容多?干脆用构建脚本(如Node.js读取JSON配置,生成.less文件)——编译期确定的事,没必要硬塞进样式语言里
真正需要“递归Mixin”的场景其实很少,多数时候是想省几行重复代码。先确认是不是真需要动态生成,再决定要不要绕过Less的设计边界。











