跳到主要内容

Mixins

从现有样式中“混合”属性

你可以混合类选择器和 ID 选择器,例如:

.a,
#b {
color: red;
}
.mixin-class {
.a();
}
.mixin-id {
#b();
}

结果为:

.a,
#b {
color: red;
}
.mixin-class {
color: red;
}
.mixin-id {
color: red;
}

在以前的版本中,mixins 调用中的括号是可选的,但是可选的括号已被弃用,并且将在未来的版本中需要。

.a();
.a; //目前可以使用,但已弃用;不要使用
.a (); //括号前的空格也已弃用

带括号的 mixins

如果要创建 mixins,但不希望该 mixins 出现在 CSS 输出中,请在 mixins 定义后加上括号。

.my-mixins {
color: black;
}
.my-other-mixin() {
background: white;
}
.class {
.my-mixin();
.my-other-mixin();
}

输出

.my-mixins {
color: black;
}
.class {
color: black;
background: white;
}

mixins 中的选择器

mixins 可以包含更多内容,而不仅仅是属性,它们还可以包含选择器。

例如:

.my-hover-mixin() {
&:hover {
border: 1px solid red;
}
}
button {
.my-hover-mixin();
}

输出

button:hover {
border: 1px solid red;
}

命名空间

如果要在更复杂的选择器中混合属性,则可以堆叠多个 ID 或类。

#outer() {
.inner {
color: red;
}
}

.c {
#outer.inner();
}

注意:传统的 Less 语法允许在命名空间和 mixins 之间使用“>”和空格。此语法已弃用并可能被删除。目前,这些做相同的事情。

#outer > .inner(); //已弃用
#outer .inner(); //已弃用
#outer.inner(); //首选

像这样对 mixins 进行命名空间设置可以减少与其它库 mixins 或用户 mixins 的冲突,但也可以是“组织”mixins 组的一种方式。

例:

#my-library {
.my-mixin() {
color: black;
}
}
//可以像这样使用
.class {
#my-library.my-mixin();
}

受保护的命名空间

如果命名空间具有保护条件,则仅在保护条件返回 true 时使用由其定义的 mixin。命名空间保护条件的评估方式与 mixins 上的保护条件完全相同,因此以下两个 mixins 的工作方式相同:

#namespace when (@mode = huge) {
.mixin() {
/* */
}
}

#namespace {
.mixin() when (@mode = huge) {
/* */
}
}

假定“default”函数对所有嵌套命名空间和 mixins 具有相同的值。

以下 mixins 永远不会被评估;其中一个保护条件保证为 false:

#sp_1 when (default()) {
#sp_2 when (default()) {
.mixin() when not(default()) {
/* */
}
}
}

!important 关键字

在 mixins 调用后使用 !important 关键字,将其继承的所有属性标记为 !important

示例:

.foo (@bg: #f5f5f5, @color: #900) {
background: @bg;
color: @color;
}
.unimportant {
.foo();
}
.important {
.foo() !important;
}

结果为:

.unimportant {
background: #f5f5f5;
color: #900;
}
.important {
background: #f5f5f5 !important;
color: #900 !important;
}

Parametric Mixins

如何向 mixins 传递参数

mixins 也可以接受参数,这些参数是在混合到选择器块时传递的变量。

例如:

.border-radius(@radius) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}

以下是如何将其混合到各种规则集中的方法:

#header {
.border-radius(4px);
}
.button {
.border-radius(6px);
}

参数化 mixins 也可以为其参数设置默认值:

.border-radius(@radius: 5px) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}

现在我们可以这样调用它:

#header {
.border-radius();
}

它将包括一个 5px 的边框半径。

你还可以使用不带参数的参数化 mixin。如果你想要隐藏规则集并将其属性包含在其它规则集中,则此功能非常有用:

.wrap() {
text-wrap: wrap;
white-space: -moz-pre-wrap;
white-space: pre-wrap;
word-wrap: break-word;
}

pre {
.wrap();
}

它将输出:

pre {
text-wrap: wrap;
white-space: -moz-pre-wrap;
white-space: pre-wrap;
word-wrap: break-word;
}

参数分隔符

参数目前可以使用分号或逗号分隔。

最初,参数只由逗号分隔,但后来添加了分号以支持将逗号分隔的列表值传递给单个参数。

注意:从 Less 4.0 开始,你可以使用括号转义 [~()] 包装列表值,例如 .name(@param1: ~(red, blue))。这类似于引号转义语法:~"quote"。这可能会使你的代码库中的分号分隔符不必要。

示例:

  • 两个参数,每个参数都包含逗号分隔的列表:.name(1, 2, 3; something, else)
  • 三个参数,每个参数都包含一个数字:.name(1, 2, 3)
  • 使用虚拟分号创建一个带有包含逗号分隔的 CSS 列表的参数的 mixins 调用:.name(1, 2, 3;)。注意:如果尾随分号看起来奇怪,你可能更喜欢:.name(~(1, 2, 3))
  • 编写逗号分隔的默认值的方法:
    • @param-values: red, blue; .name(@param1: @param-values).
    • .name(@param1: red, blue;)
    • .name(@param1: ~(red, blue))

重载 mixins

定义具有相同名称和参数数量的多个 mixins 是合法的。Less 将使用所有可应用的属性。如果你使用了一个参数的 mixin,例如 .mixin(green);,则属性将应用于所有具有一个参数的 mixin。

具有一个必选参数的所有 mixins 的属性将被使用:

.mixin(@color) {
color-1: @color;
}
.mixin(@color, @padding: 2) {
color-2: @color;
padding-2: @padding;
}
.mixin(@color, @padding, @margin: 2) {
color-3: @color;
padding-3: @padding;
margin: @margin @margin @margin @margin;
}
.some .selector div {
.mixin(#008000);
}

编译为:

.some .selector div {
color-1: #008000;
color-2: #008000;
padding-2: 2;
}

命名参数

mixins 引用可以通过它们的名称而不是位置提供参数值。任何参数都可以通过它的名称引用,它们不必按任何特定顺序:

.mixin(@color: black; @margin: 10px; @padding: 20px) {
color: @color;
margin: @margin;
padding: @padding;
}
.class1 {
.mixin(@margin: 20px; @color: #33acfe);
}
.class2 {
.mixin(#efca44; @padding: 40px);
}

编译为:

.class1 {
color: #33acfe;
margin: 20px;
padding: 20px;
}
.class2 {
color: #efca44;
margin: 10px;
padding: 40px;
}

@arguments 变量

@arguments在 mixins 内部具有特殊含义,它包含调用 mixins 时传递的所有参数。如果你不想处理单个参数,则这很有用:

.box-shadow(@x: 0, @y: 0, @blur: 1px, @color: #000) {
-webkit-box-shadow: @arguments;
-moz-box-shadow: @arguments;
box-shadow: @arguments;
}
.big-block {
.box-shadow(2px, 5px);
}

结果为:

.big-block {
-webkit-box-shadow: 2px 5px 1px #000;
-moz-box-shadow: 2px 5px 1px #000;
box-shadow: 2px 5px 1px #000;
}

高级参数和@rest变量

如果你希望 mixins 接受可变数量的参数,则可以使用...。在变量名称后使用此选项将这些参数分配给变量。

.mixin(...) {        // 匹配0-N个参数
.mixin() { // 匹配恰好0个参数
.mixin(@a: 1) { // 匹配0-1个参数
.mixin(@a: 1, ...) { // 匹配0-N个参数
.mixin(@a, ...) { // 匹配1-N个参数

此外:

.mixin(@a, @rest...) {
// @rest绑定到@a之后的参数
// @arguments绑定到所有参数
}

模式匹配

有时,你可能希望根据传递给它的参数更改 mixins 的行为。让我们从基础知识开始:

.mixin(@s, @color) {
...;
}

.class {
.mixin(@switch, #888);
}

现在假设我们希望.mixin根据传递给它的参数更改行为。

根据 @switch 的值不同,我们可以定义 .mixin 如下:

.mixin(dark, @color) {
color: darken(@color, 10%);
}
.mixin(light, @color) {
color: lighten(@color, 10%);
}
.mixin(@_, @color) {
display: block;
}

现在,如果我们运行:

@switch: light;

.class {
.mixin(@switch, #888);
}

我们将得到以下 CSS:

.class {
color: #a2a2a2;
display: block;
}

其中传递给 .mixin 的颜色被变亮了。如果 @switch 的值是 dark,结果将是一个更暗的颜色。

这里发生了什么:

  • 第一个 mixins 定义没有匹配,因为它期望第一个参数是 dark
  • 第二个 mixins 定义匹配,因为它期望 light
  • 第三个 mixins 定义匹配,因为它期望任何值。

只有匹配的 mixins 定义被使用。变量匹配并绑定到任何值。除了变量之外的任何内容都只与等于自身的值匹配。

我们还可以根据参数数量进行匹配,这是一个例子:

.mixin(@a) {
color: @a;
}
.mixin(@a, @b) {
color: fade(@a, @b);
}

现在,如果我们使用一个参数调用 .mixin,我们将得到第一个定义的输出,但如果我们使用 两个 参数调用它,我们将得到第二个定义,即将 @a 淡化为 @b

Using Mixins as Funtions

从 mixins 调用中选择属性和变量

属性/值访问器

发布于 v3.5.0

从 Less 3.5 开始,你可以使用属性/变量访问器从已评估的 mixins 规则中选择值。这可以让你像使用函数一样使用 mixin。

例如:

.average(@x, @y) {
@result: ((@x + @y) / 2);
}

div {
// 调用 mixins并查找其 "@result" 值
padding: .average(16px, 50px) [ @result];
}

结果为:

div {
padding: 33px;
}

覆盖 mixins 值

如果有多个匹配的 mixin,则会评估并合并所有规则,并返回具有该标识符的最后一个匹配值。这类似于 CSS 中的级联,它允许你“覆盖”mixins 值。

// library.less
#library() {
.mixin() {
prop: foo;
}
}

// customize.less
@import "library";
#library() {
.mixin() {
prop: bar;
}
}

.box {
my-value: #library.mixin[prop];
}

输出:

.box {
my-value: bar;
}

未命名查找

如果在 mixins 或 ruleset 调用之后不在 [@lookup] 中指定查找值,而是在 mixins 或 ruleset 调用后写 [],则 所有 值都会级联,选择最后声明的值。

意思是:上面示例中的平均值 mixins 可以写成:

.average(@x, @y) {
@result: ((@x + @y) / 2);
}

div {
// 调用 mixins并查找其最终值
padding: .average(16px, 50px) [];
}

输出相同:

div {
padding: 33px;
}

对于别名为 mixins 调用的 ruleset 或变量,相同的级联行为也是正确的。

@dr: {
value: foo;
};
.box {
my-value: @dr[];
}

这将输出:

.box {
my-value: foo;
}

将 mixins 和变量解锁到调用者范围

已弃用 - 使用属性/值访问器

在 mixins 中定义的变量和 mixins 可见,并且可以在调用者的范围内使用。只有一个例外:如果调用者包含具有相同名称的变量(包括由另一个 mixins 调用定义的变量),则不会复制变量。只有出现在调用者本地范围中的变量受到保护。从父范围继承的变量将被覆盖。

注意:此行为已弃用,将来,变量和 mixins 将不会以这种方式合并到调用者范围中。

例如:

.mixin() {
@width: 100%;
@height: 200px;
}

.caller {
.mixin();
width: @width;
height: @height;
}

结果为:

.caller {
width: 100%;
height: 200px;
}

直接在调用者范围内定义的变量无法被覆盖。但是,在调用者父范围中定义的变量没有受到保护,将被覆盖:

.mixin() {
@size: in-mixin;
@definedOnlyInMixin: in-mixin;
}

.class {
margin: @size @definedOnlyInMixin;
.mixin();
}

@size: globaly-defined-value; // 调用者父范围 - 没有保护

结果为:

.class {
margin: in-mixinsin-mixin;
}

最后,mixins 在 mixins 中定义也作为返回值:

.unlock(@value) {
// 外部mixin
.doSomething() {
// 嵌套mixin
declaration: @value;
}
}

#namespace {
.unlock(5); // 解锁doSomething mixin
.doSomething(); //嵌套mixin被复制到此处并可用
}

结果为:

#namespace {
declaration: 5;
}

递归

创建循环

在 Less 中,mixins 可以调用自身。这种递归 mixins 结合 Guard Expressions 和 Pattern Matching 可以用于创建各种迭代/循环结构。

例如:

.loop(@counter) when (@counter > 0) {
.loop((@counter - 1)); // 下一次迭代
width: (10px * @counter); // 每次迭代的代码
}

div {
.loop(5); // 启动循环
}

输出:

div {
width: 10px;
width: 20px;
width: 30px;
width: 40px;
width: 50px;
}

使用递归循环生成 CSS 网格类的通用示例:

.generate-columns(4);

.generate-columns(@n, @i: 1) when (@i =< @n) {
.column-@{i} {
width: (@i * 100% / @n);
}
.generate-columns(@n, (@i + 1));
}

输出:

.column-1 {
width: 25%;
}
.column-2 {
width: 50%;
}
.column-3 {
width: 75%;
}
.column-4 {
width: 100%;
}

Mixin Guards

当你想匹配表达式而不是简单值或元数时,守卫很有用。如果你熟悉函数式编程,你可能已经遇到过它们。

为了尽可能接近 CSS 的声明性质,Less 选择通过守卫混合来实现条件执行,而不是 if/else 语句,类似于 @media 查询特性规范。

让我们从一个例子开始:

.mixin(@a) when (lightness(@a) >= 50%) {
background-color: black;
}
.mixin(@a) when (lightness(@a) < 50%) {
background-color: white;
}
.mixin(@a) {
color: @a;
}

关键是 when 关键字,它引入了一个守卫序列(这里只有一个守卫)。现在如果我们运行以下代码:

.class1 {
.mixin(#ddd);
}
.class2 {
.mixin(#555);
}

我们将得到以下结果:

.class1 {
background-color: black;
color: #ddd;
}
.class2 {
background-color: white;
color: #555;
}

守卫比较运算符

可用于守卫的完整比较运算符列表为:>, >=, =, =<, <。此外,关键字 true 是唯一的真值,使得这两个混合物等价:

.truth(@a) when (@a) {
...;
}
.truth(@a) when (@a = true) {
...;
}

除了关键字 true 之外的任何值都是假值:

.class {
.truth(40); // 不会匹配上面的任何定义。
}

请注意,你还可以将参数与其它参数或非参数进行比较:

@media: mobile;

.mixin(@a) when (@media = mobile) {
...;
}
.mixin(@a) when (@media = desktop) {
...;
}

.max(@a; @b) when (@a > @b) {
width: @a;
}
.max(@a; @b) when (@a < @b) {
width: @b;
}

守卫逻辑运算符

你可以在守卫中使用逻辑运算符。语法基于 CSS 媒体查询。

使用 and 关键字组合守卫:

.mixin(@a) when (isnumber(@a)) and (@a > 0) {
...;
}

你可以通过用逗号 , 分隔守卫来模拟 or 运算符。如果任何一个守卫计算为 true,则被视为匹配:

.mixin(@a) when (@a > 10), (@a < -10) {
...;
}

使用 not 关键字来否定条件:

.mixin(@b) when not (@b > 0) {
...;
}

类型检查函数

最后,如果你想根据值类型匹配混合物,可以使用 is 函数:

.mixin(@a; @b: 0) when (isnumber(@b)) {
...;
}
.mixin(@a; @b: black) when (iscolor(@b)) {
...;
}

以下是基本的类型检查函数:

函数:

  • iscolor:判断是否为颜色值
  • isnumber:判断是否为数字
  • isstring:判断是否为字符串
  • iskeyword:判断是否为关键字
  • isurl:判断是否为 URL

如果你想要检查一个值是否为特定单位的数字,你可以使用以下其中之一:

  • ispixel:判断是否为像素值
  • ispercentage:判断是否为百分比值
  • isem:判断是否为 em 值
  • isunit:判断是否为单位值

Aliasing Mixins

发布 v3.5.0

将 mixins 调用分配给变量

mixins 可以分配给变量,以便作为变量调用,或者可以用于映射查找。

#theme.dark.navbar {
.colors(light) {
primary: purple;
}
.colors(dark) {
primary: black;
secondary: grey;
}
}

.navbar {
@colors: #theme.dark.navbar.colors(dark);
background: @colors[primary];
border: 1px solid @colors[secondary];
}

这将输出:

.navbar {
background: black;
border: 1px solid grey;
}

变量调用

整个 mixins 调用可以被别名,并作为变量调用。例如:

#library() {
.colors() {
background: green;
}
}
.box {
@alias: #library.colors();
@alias();
}

输出:

.box {
background: green;
}

请注意,与在根中使用的 mixins 不同,分配给变量并且 没有参数调用 的 mixins 调用始终需要括号。以下是无效的。

#library() {
.colors() {
background: green;
}
}
.box {
@alias: #library.colors;
@alias(); // 错误:无法评估变量调用 @alias
}

这是因为变量是否分配了选择器列表或 mixins 调用是不明确的。例如,在 Less 3.5+ 中,可以这样使用此变量。

.box {
@alias: #library.colors;
@{alias} {
a: b;
}
}

上述将输出:

.box #library.colors {
a: b;
}