Less 不支持原生数组和 for 循环,需用递归 mixin(兼容 3.5+)或 .each()(仅 4.0+)实现阴影阶梯;正确声明列表须用空格分隔并包裹括号,如 @shadows: (0 2px 4px) (0 4px 8px)。

Less 中没有真正的数组或 for 循环,但可以用 .each() 模拟或用递归 mixin 实现阴影阶梯
Less 本身不支持 JavaScript 那样的数组和 for 循环,也没有原生的 array 类型。所谓“定义变量数组进行循环”,实际是靠 @shadows: 0 2px 4px, 0 4px 8px, ... 这类空格分隔值 + 递归 mixin 或 .each()(需 Less 4.0+)来展开。低版本只能手写或用递归。
-
.each()是 Less 官方在 v4.0 后引入的函数式工具,必须配合@list和@key/@value使用,不能直接遍历字符串 - 常见错误:把逗号分隔的阴影值直接赋给变量(如
@shadows: 0 2px 4px, 0 4px 8px;),结果被解析成单个值而非列表 —— 正确做法是用空格分隔,并包裹在括号里:@shadows: (0 2px 4px) (0 4px 8px) (0 8px 16px); - 如果你用的是 Less 3.x(比如旧版 Vue CLI 或老 webpack loader),
.each()不可用,必须用递归 mixin
用递归 mixin 手动展开阴影阶梯(兼容 Less 3.5+)
这是最稳妥、兼容性最好的方式,核心是定义一个带索引的 mixin,每次处理列表首项,再调用自身处理剩余项。
.shadow-steps(@shadows) when (length(@shadows) > 0) {
@first: extract(@shadows, 1);
@rest: slice(@shadows, 2);
.shadow-@{length(@shadows)} {
box-shadow: @first;
}
.shadow-steps(@rest);
}
// 使用
@shadows: (0 1px 2px rgba(0,0,0,0.1)) (0 2px 4px rgba(0,0,0,0.12)) (0 4px 8px rgba(0,0,0,0.14)) (0 8px 16px rgba(0,0,0,0.16));
.shadow-steps(@shadows);
-
extract(@list, 1)取第一个阴影值;slice(@list, 2)剩余部分 —— 注意slice从 2 开始,不是 1 - 生成的 class 名如
.shadow-4、.shadow-3,数字对应原始列表长度,便于语义化使用 - 别漏掉
when (length(@shadows) > 0)守卫条件,否则无限递归报错:Recursive call to shadow-steps
用 .each() 简化(仅 Less 4.0+,如 Vite + less 4.2.0)
如果环境允许,.each() 更接近“循环”直觉,但要注意参数结构:它传入的是 @value(当前项)和 @key(从 1 开始的序号),且只接受 list 类型。
@shadows: (
(0 1px 2px rgba(0,0,0,0.1)),
(0 2px 4px rgba(0,0,0,0.12)),
(0 4px 8px rgba(0,0,0,0.14)),
(0 8px 16px rgba(0,0,0,0.16))
);
.each(@shadows, {
.shadow-@{key} {
box-shadow: @value;
}
});
- 必须用嵌套括号写法:
(...), (...)构成 list,每个元素本身是 tuple(即另一个括号包裹的值) -
@key是数字(1、2、3…),@value是对应那一组box-shadow值,无需extract - 若混用单位或漏括号,编译会静默失败或报
Operation on an invalid type
生成的 CSS 要兼顾可维护性和性能
批量生成阴影 class 看似方便,但容易产出冗余样式。真正关键的是:是否所有阶梯都真正在项目中被用到?
- 建议按用途分层命名,比如
.shadow-sm/.shadow-md/.shadow-lg,而不是只靠数字序号 —— 数字无法传达设计意图 - 避免在 :hover/:focus 等伪类里重复定义整套阴影,应复用已生成的 class,而非在 mixin 里嵌套生成
- 如果项目用 CSS-in-JS 或 Tailwind,这类 Less 阶梯意义不大 —— Tailwind 的
shadow-sm到shadow-2xl已覆盖常见需求,且按需生成
递归 mixin 看似麻烦,但它让你完全掌控输出结构;.each() 更简洁,但对 Less 版本和语法格式极其敏感。选哪个,取决于你锁死的构建链路 —— 兼容性问题往往比语法糖更重要。

















