Less 中无 for 循环,需用递归 mixin 模拟编译期展开;each() 可遍历 map 生成语义化间距类,但易致 CSS 膨胀,建议限定范围、加前缀、用 !default 留扩展钩子。

Less 中没有 for 循环,但可以用 .loop() 递归混合(mixin)模拟
Less 本身不支持 for 或 while 这类原生循环语句,强行写会报错 ParseError: expected ')' got 'for'。实际能用的,是通过带参数的 mixin 自调用实现递归展开——本质是编译期展开,不是运行时循环。
常见错误是直接照搬 JavaScript 思维写 for (let i = 0; i ,结果 Less 编译器根本识别不了。
实操建议:
- 定义一个递归 mixin,比如
.generate-margins(@i: 1, @n: 12),用@i当前值和@n上限控制终止条件 - 每次递归生成一条规则,如
.m-@{i} { margin: unit(@i * 4, px); } - 递归调用写在规则之后、
.when (@i 守卫条件里,避免无限展开 - 注意单位计算:直接写
@i * 4px会被 Less 当作字符串拼接,要用unit(@i * 4, px)或(@i * 4)px
生成响应式间距类时,必须手动分层写多套 .loop()
想让 .m-md-2 在中屏生效、.m-lg-4 在大屏生效?Less 不会自动为每个断点重复生成整套类名。你得为每个断点单独调用一遍递归 mixin,并包裹在媒体查询里。
立即学习“前端免费学习笔记(深入)”;
容易踩的坑是试图用一个 mixin 同时处理“数值”和“断点”,比如传入 @breakpoint: md 然后拼出 m-@{breakpoint}-@{i} ——这语法合法,但无法自动注入对应 @media 规则,仍需人工分层。
实操建议:
- 先定义断点变量:
@md: ~"(min-width: 768px)"; @lg: ~"(min-width: 992px)"; - 为每个断点单独写一个调用,例如:
.generate-margins-md() { .generate-margins(1, 8) } @media @md { .generate-margins-md(); } - 类名拼接用字符串插值
m-@{bp}-@{i}没问题,但别指望它能跨媒体查询复用同一组数值逻辑
用 map 配置间距步长比硬编码数字更灵活
如果项目需要支持「紧凑模式」(2px 步进)、「舒适模式」(8px 步进)、「宽松模式」(16px 步进),把所有数值写死在 .loop() 里会导致维护困难。
更好的做法是用 Less map 存储配置,例如:@spacers: (xs: 0.25, sm: 0.5, md: 1, lg: 1.5, xl: 3);,再用 each() 遍历(Less 3.5+ 支持)。
注意:each() 是 Less 内置函数,不是自定义 mixin,它会在编译时展开 map 的每一项,比手写多重递归更清晰。
实操建议:
- 写法示例:
each(@spacers, {@value, @key} => { .m-@{key} { margin: unit(@value * 1rem, rem); } }); -
@key是 map 键(如sm),@value是对应倍数(如0.5),适合语义化命名 - 不支持嵌套 map,也不支持动态 key 名;若需同时生成
mt-、mb-等方向类,仍要配合 mixin 嵌套或重复写几遍each()
编译后 CSS 体积会随生成数量线性增长,慎用超大范围
生成从 .m-1 到 .m-100 看似全面,但实际会产出 100 条独立规则,gzip 后可能增加 2–3KB CSS 体积。对首屏渲染和缓存效率都有影响。
更严重的是,如果又叠加 4 个方向(mt/mr/mb/ml)、3 个断点、2 种单位(px/rem),组合爆炸会让文件迅速膨胀。
实操建议:
- 优先覆盖 80% 场景:通常
0–8(步长 4px)或0–5(步长 0.25rem)已够用 - 用
!default给变量留钩子,方便下游项目覆盖:@spacer-count: 8 !default; - 生成类名时加前缀(如
u-m-)避免和业务类冲突,也便于 PurgeCSS 识别并移除未使用类
真正麻烦的不是怎么写循环,而是想清楚哪些间距值是设计系统真正需要的——自动生成容易,删掉冗余类却要翻遍所有模板。


















