Sass3.3新特性之连体符&

Hugo Giraudel在《Looking Into the Future of Sass》(译文Sass新特性)一文中向大家介绍了Sass的十大新特性。这些特性都将让你的工作变得更简单与方便。那么今天我主要想向大家介绍我自己对Sass3.3中几个新特性的理解与使用心得,希望对大家的学习有所帮助。

在Sass中使用连体符&

在Sass3.3中,连体符&让你的选择器变得更简单,维护更方便。他可以轻松的让你选择父元素,可以配合伪类元素等(详细请参考《Sass中连体符(&)的运用》)。但在Sass中还有一个新的用法,如:

$selector: null;
.block{
    $selector: &;
}
#{$selector}--map {
    color: green;
}

编译出来的CSS:

.block--map {
  color: green; 
}

但此时Sass编译器将报出错误信息:

DEPRECATION WARNING on line 6 of test.scss:
Assigning to global variable "$selector" by default is deprecated.
In future versions of Sass, this will create a new local variable.
If you want to assign to the global variable, use "$selector: & !global" instead.

根据报警提示,我们将上面的代码进行修改,在代码中加入!global:

.block{
    $selector: & !global;
}
#{$selector}--map {
    color: green;
}

编译出来的CSS:

.block--map {
  color: green; 
}

和第一种方法编译出来的CSS是一样的。此时试想,如果我们的代码中有不同的BEM命名方式,比如在CSS中有这样的一段代码:

.view-element {...}
.block-element {...}

根据前面的示例,大家可能第一时间想到是:

.view,
.block {
    $selector: & !global;
}
#{$selector}--element{
    color:green;
}

但这段SCSS代码编译出来的CSS并不是我们想要的代码:

.block, 
.view--element {
  color: green; 
}

或许你会考虑这样来重构SCSS代码:

.block{
    $selector: & !global;
}
.view {
    $selector: & !global;
}
#{$selector}--element {
    color: green;
}

但编译出来的CSS还不是你想要得到的:

.view--element {
  color: green; 
}

很明显.view覆盖了.block中的$selector: & !global。但仍不死心,在nex3/sass上提了一个issues@lolmaus提供了一种方法:

.block,
.view {
    $selectors: & !global;
}
@each $selector in $selectors {
    #{$selector}--element {
        color: green;
    }
}

编译出来CSS:

.block--element {
  color: green; 
}

.view--element {
  color: green; 
}

这似乎是我们想要的CSS代码,但换一个条件:

.block-element {color:green;}
.view-element {color: blue;}

那么目前为止,在Sass使用!global中还无法实现。只能变换一种方式:

$selector: null;
.block {
    $selector: &;
}
#{$selector}--element{
    color: green;
}
.view {
    $selector: &;
}
#{$selector}--element{
    color:blue;
}

这样编译出来的CSS是我们所需要的:

.block--element {
  color: green; 
}

.view--element {
  color: blue; 
}

虽然实现我们需要的效果,但似乎这样的SCSS代码并没有给我们的工作带来方便。当然,在Sass中还有一种更容易实现的方法,这也是Sass3.3中的另一个新特性,稍后需要描述的。不过在这里,通过多种方式演示,唯一想说明的一点是在Sass3.3中新引入了一个!global,它可以用来定义某个选择器为全局选择器变量或者定义全局变量。等同效果:

//方法一
$selector: '.selector';
#{$selector}--element {
    background-color: #fcc;
}
//方法二
.selector {
    $selector: & !global;
}
#{$selector}--element {
    background-color: #fcc;
}

CSS:

.selector--element {
  background-color: #fcc; 
}

但有一点需要特别声明,!global!default并不相同。

!defaultVS !global

!default在Sass中常称为变量默认值,而!global在Sass中称为变量全局值。这好象又回到了全局变量和局部变量的争论中了。有关于这两个方面的争论,我们避而不谈,主要还是来看看他们功能上的区别。

首先我们简单的回忆一下这样的一个示例:

p {
    $color: green;
    color: $color;
    a {
        color: $color;
    } 
}

div {
    color: $color;
}

在编译Sass时将会报错:

error test.scss (Line 16: Undefined variable: "$color".)

错误信息告诉我们,在div中的$color是没有定义的一个变量值。为了能让代码正常运行,必须先得在外面显式的声明$color变量。

$color: blue;

p {
    $color: green;
    color: $color;
    a {
        color: $color;
    } 
}

div {
    color: $color;
}

编译出的CSS:

p {
  color: green; 
}
p a {
  color: green; 
}

div {
  color: green; 
}

那么在Sass3.3中,我们可以借助于!global在任何地方定义一个变量,而且任何一组代码中都可以调用这个变量:

p {
    $color: green !global;
    color: $color; //输出green
    a {
        color: $color; //输出green
    } 
}

div {
    color: $color; //输出green
}

使用!global之后,将不会报错,编译出来的CSS也无误。

另外在使用!global依旧无法避免顺序造成的覆盖,我们来尝试一个新示例:

p {
    $color: green !global;
    color: $color; //输出green
    a {
        $color: red;
        color: $color; //输出red
    } 
}

div {
    color: $color; //输出red
}

回到!default中,大家都知道同一变量后面出现的总是会覆盖前面出现的:

$color: blue;
$color: red ;

p {
    color: $color; //输出red
    a {
        color: $color; //输出red
    } 
}

div {
    color: $color; //输出red
}

一旦在第二个变量加上!default之后,那么一切将有可能改变:

$color: blue;
$color: red !default;

p {
    color: $color; //输出blue
    a {
        color: $color; //输出blue
    } 
}

div {
    color: $color; //输出blue
}

但要是将后面出现的$color变量后添加!global,那依旧是后者覆盖前者:

$color: blue;
$color: red !global;

p {
    color: $color; //输出red
    a {
        color: $color; //输出red
    } 
}

div {
    color: $color; //输出red
}

而这种方式也将无任何意义。

通过这些对比,我们可以得出:!default可以用来设置一个默认变量,其不会覆盖前面设置的相同变量;而!global可以在任何代码块中声明一变量,提供给其他代码块调用此变量。

备注:有关于!default的相关介绍,大家要是感兴趣的话可以猛击《Sass揭秘之变量》一文。

&另一用法#{&}

在Sass中&配上#{}可以做为选择器插值。当然,这个在生成后代选择器的时候非常方便,简单的看个示例:

block {
    #{&} .bar {
        color: red;
    }
}

编译出CSS:

.block .block .bar {
  color: red; 
}

似乎把我们的CSS变得更为复杂,但其还是具有一定的优势的,比如在BEM的应用中:

.block {
    #{&}--element {
        color: red;
    }
}

编译出CSS:

.block .block--element {
  color: red; 
}

编译出来的样式代码,虽然离BEM的初衷甚远,但也免强可一用。不过在Sass3.3中有一个新的特性@at-root可以更好的配合BEM一起使用。

@at-root

在Sass中,如果你不想嵌套选择器,可以使用@at-root特性。当像使用#{&}可以明确的告诉Sass,他只是一个变量,而不会嵌套。来看一个示例:

.block {
    @at-root #{&}__element {
        color: red;
    }
    @at-root #{&}--modifier{
        color:green;
    }
}

编译出CSS:

.block__element {
  color: red; 
}
.block--modifier {
  color: green; 
}

生成的CSS代码正是我们想要的BEM模式。也就是说,有了@at-root可以非常简单的实现BEM模式。在Sass中除了这种方式之外,还可以将@at-root以块的方式展示:

.block {
    @at-root {
        #{&}__element {
            color: red;
        }
        #{&}--modifier{
            color:green;
        }
    }
}

同样可以编译出来上一段相同的CSS代码。当然@at-root的实际应用不只这些,有关于更详细的使用,可以猛击《Sass @at-root》一文。

总结

在《Sass中连体符(&)的运用》一文中介绍了Sass中&的使用。其实&是Sass3.3中十大特性之一。他可以让你的选择器嵌套更便捷,而且可以配合#{}实现选择器的插值。另外通过与!global的配合可以指引定选择器为变量。另外最大的优势是,配合@at-root可以与BEM完美的结合,真正实现BEM的设计模式。

希望这些简单的示例,能帮助大家更好的理解&和更好的使用&符。如果您有更好的建议和使用经验,可以在下面的评论中与我们一起分享。

出处:http://www.w3cplus.com/preprocessor/sass-future-use-ampersand.html