使用Sass减少重复性代码

Hugo大师在understand Sass listsADVANCED 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);
}

扩展阅读

总结

本文通过Sass的@each@for@function不同方法,有效的实现快速编码和减速无用的CSS,从而降低CSS文件大小。也更易于维护CSS文件。如果您对此有更好的处理方式,欢迎在下面的评论与我们一起分享。

出处:http://www.w3cplus.com/preprocessor/user-sass-reduce-repetitive-code.html