Hugo大师在understand Sass lists和ADVANCED SASS LIST FUNCTIONS两篇文章中详细分析了Sass中list的功能,但在Sass中还有一个特性非常的有意思——变量插值。不管是列表,还是变量甚至说@if
,@for
,@each
都是Sass中基础部分,仅从一个方面来看,并不能展示出Sass的魅力。反过来说,如果能将这几个部分结合在一起,那么将会给你的开发带来强大的方便性。今天我们就以Sass的变量作为话题的开端,来讨论Sass中变量配合其他功能另一用法。
在CSS的开发中,我们都在讨论OOCSS的使用,但其最终还是无法逃脱创建一些不使用的CSS代码。我们希望的是使用这些类产生的样式,需要的时候就产生CSS代码,不需要的时候就不产生CSS代码。希望做到的是真正按需生产,不产生额外的CSS代码。
来看一个典型的案例——Font Awesome图标制作,为了使用这个图标库里面的图标,在CSS的常规制作中,我们必须得将其样式导入进来。可是,很多时候,我们只需要其中的几个图标,换句话说,我只想给要的图标在样式中产生代码。在CSS中,我们无法实现,为了使用这个库,我们必须添加所有的图标类以及对应的相关代码。如此一来造成很多不需要的代码。
.icon-glass:before {
content: "\f000";
}
.icon-music:before {
content: "\f001";
}
.icon-search:before {
content: "\f002";
}
.icon-envelope-o:before {
content: "\f003";
}
.icon-heart:before {
content: "\f004";
}
.icon-star:before {
content: "\f005";
}
...
那么今天,我们就要来解决这个问题。
所需要的参数
欲解决上面提到的问题,我们在具体介绍详细的解决方案之前,我们需要一些参数。在Sass中,只要未调用,他是不会产生额外代码。此处我们主要借助Sass中的变量和列表来发力。
变量
大家都知道Font Awesome图标是通过字体来实现的,而其中每个图标都有其自身的实体编码,如"\f000"
、"\f001"
等等。当中就会有很多重复的代码,需要修复这些重复代码,我们就需要通过变量列表。首要条件是我们需要声明一些变量,我们把这些变量放在_variables.scss
文件中。
$icon-glass: "\f000";
$icon-music: "\f001";
$icon-search: "\f002";
$icon-envelope-o: "\f003";
$icon-heart: "\f004";
$icon-star: "\f005";
列表
接下来,我们需要一个列表,方便后面的遍历。注意,变量插值不能直接通过Sass的内部指令来控制。我想要一个带有选择器和名称和变量的列表,可在Sass中(至少目前为止)还不支持这样的特性。为了实现这样这一点,我们不得不对代码做一个变通。
$icon_names: icon-glass icon-music icon-search icon-envelope-o icon-heart icon-star;
$icon_vars: $icon-glass $icon-music $icon-search $icon-envelope-o $icon-heart $icon-star;
解决方法
接下来,我们来看Sass中是如何解决这个问题,让你的代码在需要的时候产生。也就是说调用的时候就产生,不调用的时候不产生额外的CSS代码。本文主要通过以下几种方法来实现:
@each
方法
第一种文案是采用Sass的列表遍历功能,使用变量$icon_names
做类名遍历:
@each $name in $icon_names {
//在这进行操作
}
这里使用了Sass的@each
循环,在此处定义了一个新的变量$name
。让变量$name
在$icon_names
进行遍历。在我们的示例中,$name
会遍历$icon_nam
列表中的每个值。在此处一共会做五次遍历。
在循环中我们要添加一些动作,让$name
变成我们需要的东西。简单点说,$name
遍历的是$icon_names
列表,每遍历一次就得到我们需要的一个类名,比如说.icon-glass
。在接下的来的例子中,我们使用.#{$name}
来得到类名。并且在其中添加样式:
@each $name in $icon_names {
.#{$name} {
content: $icon_vars;
}
}
上面的循环中,我们通过$name
变量的@each
功能,使用$icon_vars
得到需要的实体符。将上面的代码进行编译:
.icon-glass {
content: "\f000" "\f001" "\f002" "\f003" "\f004" "\f005"; }
.icon-music {
content: "\f000" "\f001" "\f002" "\f003" "\f004" "\f005"; }
.icon-search {
content: "\f000" "\f001" "\f002" "\f003" "\f004" "\f005"; }
.icon-envelope-o {
content: "\f000" "\f001" "\f002" "\f003" "\f004" "\f005"; }
.icon-heart {
content: "\f000" "\f001" "\f002" "\f003" "\f004" "\f005"; }
.icon-star {
content: "\f000" "\f001" "\f002" "\f003" "\f004" "\f005"; }
很接近我们需要的,但我们每一个类只需要对应的一个实体符,因此我们还需要做一定的处理。在这里我们创建一个新的变量$i
。Sass将通过index()
函数,遍历$color_names
中的$name
,得到对应的索引值,并赋予给变量$i
:
$i: index($icon_names, $name);
现在我们已经得到了索引号,我们可以使用这个值遍历这两个列表。使用Sass的nth()
函数,我们可以指定一个列表,然后使用$i
值,指定列表中的一个特定的值给CSS的content
属性。
@each $name in $icon_names {
$i: index($icon_names, $name);
.#{$name} {
content: nth($icon_vars, $i);
}
}
上面的意思就是,指定列表$icon_vars
中第$i
个值给contentn
属性。我们来看一个编译出来的CSS:
.icon-glass {
content: "\f000"; }
.icon-music {
content: "\f001"; }
.icon-search {
content: "\f002"; }
.icon-envelope-o {
content: "\f003"; }
.icon-heart {
content: "\f004"; }
.icon-star {
content: "\f005"; }
这就是我们需要的,但不是最理想的。
接下来,我们通过Sass的%placeholder
和@extend
来创建一些占伴符。通过对%placeholder
的介绍得知,%placeholder
只是一个占位符,默认情况下是不产生任何CSS代码,这不正是我们想要的效果吗?
@each $name in $icon_names {
$i: index($icon_names, $name);
%#{$name} {
content: nth($icon_vars, $i);
}
}
上面的代码编译后是得不到任何的CSS代码,要想上面的代码产生对应的CSS代码,我们需要在上面的代码基础上使用@extend
扩展对应的%placeholder
。在这个例子中,我们将看到再次遍历$icon_vars
的值:
@each $name in $icon_names {
$i: index($icon_names, $name);
.#{$name} {
@extend %#{nth($icon_names, $i)};
}
}
最终的代码:
$icon-glass: "\f000";
$icon-music: "\f001";
$icon-search: "\f002";
$icon-envelope-o: "\f003";
$icon-heart: "\f004";
$icon-star: "\f005";
$icon_names: icon-glass icon-music icon-search icon-envelope-o icon-heart icon-star;
$icon_vars: $icon-glass $icon-music $icon-search $icon-envelope-o $icon-heart $icon-star;
@each $name in $icon_names {
$i: index($icon_names, $name);
%#{$name} {
content: nth($icon_vars, $i);
}
}
@each $name in $icon_names {
$i: index($icon_names, $name);
.#{$name} {
@extend %#{nth($icon_names, $i)};
}
}
编译出来的CSS:
.icon-glass {
content: "\f000"; }
.icon-music {
content: "\f001"; }
.icon-search {
content: "\f002"; }
.icon-envelope-o {
content: "\f003"; }
.icon-heart {
content: "\f004"; }
.icon-star {
content: "\f005"; }
@for
方法
在这个示例中,我们除了使用@each
方法之外,还可以使用@for
循环方法来处理。在@for
循环的方法中,使用length()
替代index()
。
@for $i from 1 through length($icon_names) {
%#{nth($icon_names, $i)} {
content: nth($icon_vars, $i);
}
}
@for $i from 1 through length($icon_names) {
.#{nth($icon_names, $i)} {
@extend %#{nth($icon_names, $i)};
}
}
@function
方法
$icon_vars
可以通过创建一个函数功能进行查询。在我们的示例中,在$icon_vars
中查询出对应的$icon_names
对应的内容。
@function get-icon($search) {
$index: index($icon_names, $search);
@return nth($icon_vars, $index);
}
在这个示例中,$search
是我们在$icon_names
查找的icon对应的类名。相对应函数返回的icon对应的实体符。我们可以使用一种简单的方法直接在函数中嵌套index()
函数。
@function get-icon($search) {
@return nth($icon_vars, index($icon_names, $search));
}
使用@function
方法,同样在默认情况下是不会有任何的样式产生的,只有通过下面的方式调用的时候才会产生代码:
.icon-glass {
content: get-icon("icon-glass");
}
编译出来的CSS:
.icon-glass {
content: "\f000"; }
这种方法才是我们真正想要的方式,真正叫按需产生。
除了上述的方法之外,我们还可以结合前面的知识做一个调整。
我可以单独的以key=>value
的方式创建一个简单的Sass列表,例如:
$icons:
glass "\f000",
music "\f001",
search "\f002",
envelope-o "\f003",
heart "\f004",
star "\f005"
;
同样,我们可以通过nth()函数功能,从$icons
中检索中数字检索出对应的值:
.icon-glass {
content: nth($icons,1);
}
编译出来的CSS:
.icon-glass {
content: glass "\f000"; }
这样的结果并不是我们需要的。为了解决这个问题,我们需要创建一个Sass函数,返回一个键名key
和其对应的值value
。也就是前面所说的key=>value
所对应的效果:
@function get-icon($icon-name){
//$icons列表做循环
@each $icon in $icons {
//如果关键词key(第一个元素是当前的$icon)一样,参数指定返回的值
@if nth($icon, 1) == $icon-name {
@return nth($icon,2);
}
}
}
现在,我们可以使用get-icon
函数返回需要的值,达到按需产出:
.icon-glass {
content: get-icon(glass);
}
编译出来的CSS:
.icon-glass {
content: "\f000"; }
最后列出,这种方法所使用的所有Sass代码:
$icons:
glass "\f000",
music "\f001",
search "\f002",
envelope-o "\f003",
heart "\f004",
star "\f005"
;
@function get-icon($icon-name){
@each $icon in $icons {
@if nth($icon, 1) == $icon-name {
@return nth($icon,2);
}
}
}
.icon-glass {
content: get-icon(glass);
}