Less 循环 #

Less 没有像 Sass 那样的 @for@each@while 循环语法,但可以通过递归 Mixin 实现循环功能。

递归 Mixin #

基本原理 #

递归 Mixin 是指在 Mixin 内部调用自身:

less
.loop(@n) when (@n > 0) {
  .loop(@n - 1);  // 递归调用
  width: @n * 10px;
}

.box {
  .loop(3);
}

编译结果:

css
.box {
  width: 10px;
  width: 20px;
  width: 30px;
}

简单计数循环 #

less
.generate-columns(@n, @i: 1) when (@i =< @n) {
  .col-@{i} {
    width: percentage(@i / @n);
  }
  .generate-columns(@n, @i + 1);  // 递归
}

.generate-columns(12);

编译结果:

css
.col-1 { width: 8.33333333%; }
.col-2 { width: 16.66666667%; }
.col-3 { width: 25%; }
.col-4 { width: 33.33333333%; }
.col-5 { width: 41.66666667%; }
.col-6 { width: 50%; }
.col-7 { width: 58.33333333%; }
.col-8 { width: 66.66666667%; }
.col-9 { width: 75%; }
.col-10 { width: 83.33333333%; }
.col-11 { width: 91.66666667%; }
.col-12 { width: 100%; }

循环模式 #

递增循环 #

less
.increment(@start, @end, @step: 1) when (@start =< @end) {
  .item-@{start} {
    width: @start * 10px;
  }
  .increment(@start + @step, @end, @step);
}

.increment(1, 5);

编译结果:

css
.item-1 { width: 10px; }
.item-2 { width: 20px; }
.item-3 { width: 30px; }
.item-4 { width: 40px; }
.item-5 { width: 50px; }

递减循环 #

less
.decrement(@n) when (@n > 0) {
  .level-@{n} {
    font-size: @n * 4px;
  }
  .decrement(@n - 1);
}

.decrement(6);

编译结果:

css
.level-6 { font-size: 24px; }
.level-5 { font-size: 20px; }
.level-4 { font-size: 16px; }
.level-3 { font-size: 12px; }
.level-2 { font-size: 8px; }
.level-1 { font-size: 4px; }

实用示例 #

网格系统 #

less
@columns: 12;
@gutter: 30px;

.generate-grid(@n, @i: 1) when (@i =< @n) {
  .col-@{i} {
    width: percentage(@i / @n);
  }
  .col-offset-@{i} {
    margin-left: percentage(@i / @n);
  }
  .generate-grid(@n, @i + 1);
}

.generate-grid(@columns);

间距工具类 #

less
@spacing-unit: 8px;
@spacing-levels: 6;

.generate-spacing(@n, @i: 0) when (@i =< @n) {
  .p-@{i} { padding: @i * @spacing-unit; }
  .m-@{i} { margin: @i * @spacing-unit; }
  
  .pt-@{i} { padding-top: @i * @spacing-unit; }
  .pr-@{i} { padding-right: @i * @spacing-unit; }
  .pb-@{i} { padding-bottom: @i * @spacing-unit; }
  .pl-@{i} { padding-left: @i * @spacing-unit; }
  
  .mt-@{i} { margin-top: @i * @spacing-unit; }
  .mr-@{i} { margin-right: @i * @spacing-unit; }
  .mb-@{i} { margin-bottom: @i * @spacing-unit; }
  .ml-@{i} { margin-left: @i * @spacing-unit; }
  
  .px-@{i} {
    padding-left: @i * @spacing-unit;
    padding-right: @i * @spacing-unit;
  }
  
  .py-@{i} {
    padding-top: @i * @spacing-unit;
    padding-bottom: @i * @spacing-unit;
  }
  
  .mx-@{i} {
    margin-left: @i * @spacing-unit;
    margin-right: @i * @spacing-unit;
  }
  
  .my-@{i} {
    margin-top: @i * @spacing-unit;
    margin-bottom: @i * @spacing-unit;
  }
  
  .generate-spacing(@n, @i + 1);
}

.generate-spacing(@spacing-levels);

字体大小 #

less
@font-sizes: 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48;

.generate-font-size(@list, @i: 1) when (@i =< length(@list)) {
  @size: extract(@list, @i);
  
  .text-@{size} {
    font-size: unit(@size, px);
  }
  
  .generate-font-size(@list, @i + 1);
}

.generate-font-size(@font-sizes);

编译结果:

css
.text-10 { font-size: 10px; }
.text-12 { font-size: 12px; }
.text-14 { font-size: 14px }
.text-16 { font-size: 16px; }
.text-18 { font-size: 18px; }
.text-20 { font-size: 20px; }
.text-24 { font-size: 24px; }
.text-28 { font-size: 28px; }
.text-32 { font-size: 32px; }
.text-36 { font-size: 36px; }
.text-48 { font-size: 48px; }

阴影层级 #

less
.generate-shadows(@n, @i: 1) when (@i =< @n) {
  .shadow-@{i} {
    box-shadow: 0 @i * 2px @i * 4px rgba(0, 0, 0, 0.1);
  }
  .generate-shadows(@n, @i + 1);
}

.generate-shadows(5);

编译结果:

css
.shadow-1 { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
.shadow-2 { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); }
.shadow-3 { box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); }
.shadow-4 { box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); }
.shadow-5 { box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); }

Z-Index 层级 #

less
@z-indexes: dropdown, sticky, fixed, modal, popover, tooltip;
@z-index-base: 1000;

.generate-z-index(@list, @i: 1) when (@i =< length(@list)) {
  @name: extract(@list, @i);
  
  .z-@{name} {
    z-index: @z-index-base + @i * 100;
  }
  
  .generate-z-index(@list, @i + 1);
}

.generate-z-index(@z-indexes);

编译结果:

css
.z-dropdown { z-index: 1100; }
.z-sticky { z-index: 1200; }
.z-fixed { z-index: 1300; }
.z-modal { z-index: 1400; }
.z-popover { z-index: 1500; }
.z-tooltip { z-index: 1600; }

断点媒体查询 #

less
@breakpoints: {
  sm: 576px;
  md: 768px;
  lg: 992px;
  xl: 1200px;
  xxl: 1400px;
};

.generate-breakpoints(@breakpoints);

使用 each 函数(Less 3.7+):

less
@breakpoints: {
  sm: 576px;
  md: 768px;
  lg: 992px;
  xl: 1200px;
};

each(@breakpoints, {
  @media (min-width: @value) {
    .container-@{key} {
      max-width: @value;
    }
  }
});

动画关键帧 #

less
.generate-keyframes(@name, @from, @to) {
  @keyframes @name {
    from { @from(); }
    to { @to(); }
  }
}

.generate-keyframes(fade-in, 
  { opacity: 0; },
  { opacity: 1; }
);

.fade-in {
  animation: fade-in 0.3s ease;
}

颜色变体 #

less
@colors: primary, secondary, success, danger, warning, info;
@color-values: {
  primary: #007bff;
  secondary: #6c757d;
  success: #28a745;
  danger: #dc3545;
  warning: #ffc107;
  info: #17a2b8;
};

.generate-buttons(@list, @i: 1) when (@i =< length(@list)) {
  @name: extract(@list, @i);
  @color: @color-values[@name];
  
  .btn-@{name} {
    background: @color;
    color: contrast(@color);
    border-color: darken(@color, 5%);
    
    &:hover {
      background: darken(@color, 10%);
    }
  }
  
  .btn-outline-@{name} {
    background: transparent;
    color: @color;
    border: 1px solid @color;
    
    &:hover {
      background: @color;
      color: contrast(@color);
    }
  }
  
  .generate-buttons(@list, @i + 1);
}

.generate-buttons(@colors);

each 函数 #

Less 3.7+ 提供了 each 函数,简化循环操作:

遍历列表 #

less
@list: blue, red, green, yellow;

each(@list, {
  .bg-@{value} {
    background: @value;
  }
});

编译结果:

css
.bg-blue { background: blue; }
.bg-red { background: red; }
.bg-green { background: green; }
.bg-yellow { background: yellow; }

遍历映射 #

less
@colors: {
  primary: #007bff;
  success: #28a745;
  danger: #dc3545;
};

each(@colors, {
  .text-@{key} {
    color: @value;
  }
  .bg-@{key} {
    background: @value;
  }
});

编译结果:

css
.text-primary { color: #007bff; }
.bg-primary { background: #007bff; }
.text-success { color: #28a745; }
.bg-success { background: #28a745; }
.text-danger { color: #dc3545; }
.bg-danger { background: #dc3545; }

使用索引 #

less
@list: 10px, 20px, 30px;

each(@list, {
  .spacing-@{index} {
    padding: @value;
  }
});

编译结果:

css
.spacing-1 { padding: 10px; }
.spacing-2 { padding: 20px; }
.spacing-3 { padding: 30px; }

最佳实践 #

使用有意义的变量名 #

less
// 推荐
.generate-columns(@column-count, @current: 1) when (@current =< @column-count) {
  // ...
}

// 不推荐
.loop(@n, @i: 1) when (@i =< @n) {
  // ...
}

限制循环深度 #

less
// 推荐:设置合理的上限
.generate-spacing(@n, @i: 0) when (@i =< @n) and (@i < 10) {
  // ...
}

使用 each 简化代码 #

less
// 推荐:使用 each
each(@colors, {
  .text-@{key} { color: @value; }
});

// 不推荐:复杂的递归
.generate-colors(@keys, @i: 1) when (@i =< length(@keys)) {
  // ...
}

下一步 #

现在你已经掌握了循环,接下来学习 映射 来管理复杂数据结构!

最后更新:2026-03-28