CSS预处理器之sass/scss

1 SCSS/SASS

两者在使用上,稍有不同, 主要体现在格式上,SCSS 的格式和 CSS 格式几乎一样,不局限于缩进,带有大括号,而 SASS 必须严格遵循缩进原则,并且没有大括号,使用上选择SCSS 更多点,需要注意的是两者不能混用,也就是说后缀名为 .scss 的书写规则就不能在其中使用 SASS 书写规则,否则会出现意想不到的错误,慎重选择。

这里主要以 SCSS 为主,原因很简单,更能接收点。

2 SASS 的安装

不管是 SCSS 还是 SASS 都可以通过 sass 命令来翻译等其他操作,由于sass 是基于 Ruby 实现的,所以在安装的时候直接输入 sudo apt-get install sass 的时候会提示使用 ruby-sass 进行安装,这个无所谓,它会自动帮助我们选择 ruby-sass 进行安装;

安装好之后使用 sass --version 查看下,如果能正确输出版本号,说明安装成功

3 使用 sass 命令

编译:

sass src.scss:dist.css

需要注意的是,源文件和目标文件是采用冒号(“:”)来隔开,如果想监听文件的变动,可以加上 --watch 参数

sass --watch src.scss:dist.css

这样一旦 src.scss 有变动,dist.css 文件就会实时发生变化。

还可以通过 sass --help 来查看更多相关的使用。

4 语法

4.1 变量声明

变量声明使用 ‘$’ 美元符号作为前缀来声明,并且还支持作用域,即可以定义全局和局部变量,两者定义方式,全局定义在整个文件范围,局部则是定义在大括号中的变量。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
// test.scss

$bgColor: red;

body {
background-color: $bgColor;
}

a {
$mtop: 10px;

}

上面的 $bgColor 就是全局变量,再看 $mtop 就是局部变量,只在 a 标签中生效。

变量使用:很简单,直接通过 $var 使用即可,如上的背景色设置;

4.2 父选择器引用:&

通过 ‘&’ 符号可以在大括号内引用父选择器,从而做出更丰富的选择器操作,比如:

1
2
3
4
5
6
7
// test.scss

a {
&:hover {
color: red;
}
}

上面示例中,相对第一层大括号内的选择器,a 标签就是父选择器,在里面使用 & 来引用,上面的代码实际上可以写成: a:hover { color: red; }

PS: &@at-root 结合使用,将会有意想不到的结果出现,功能强大,组合形式多样。

4.3 选择器嵌套

选择器嵌套赋予了代码编写的灵活性,嵌套主要发生在父子及子孙选择器上,如下:

1
2
3
4
5
6
7
8
9
10
// test.scss

div {

a {
img {
....
}
}
}

上面的示例就类似: div a img { ... }

群组选择器嵌套,表示多个选择器包含同一种子选择器的时候,就可以使用群组嵌套,如下:

1
2
3
4
5
6
7
// test.scss

div, p {
a {
text-decoration: none;
}
}

上面的意思是,在 divp 元素下同时都有 a 链接标签,并且要求有共同的部分样式,实际上等同于如下写法:

div a, p a { text-decoration: none; }

另外还有一些其他类型的选择器,比如: 直接子选择器(>),相邻选择器(+),兄弟选择器(~)等等

sass 下嵌套写法,也都很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// test.scss

div {

> p {

// 子选择器
}

~ span {
// 兄弟选择器,即跟在当前 div 后面的所有同级的 span 标签
}

+ div {
// 相邻选择器
}
}

对应 css 格式,如下:

div > p {} : 子标签 p,不包含孙子标签;

div ~ span {}div 标签后面的所有同级标签 span

div + div {}div 后面的相邻的 div 标签。

4.4 混合器使用

混合器,类似于函数的使用,可以通过 @mixin 定义一样式块,然后在样式表中使用 @inclue 引用该样式块。

混合器可以带参数,也可以不带参数使用,但是建议需要参数的时候使用混合器,因为后面还有更好的替代方式来使用不带参数的情况,比如:继承和 % 形式。

混合器使用:

  • 不带参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // test.scss

    @mixin set_bg {

    background-color: red;
    background-image: url(girl.png);
    background-size: 300px 300px;
    }

    // 引用

    div {
    @include set_bg;
    }
  • 带参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // test.scss
    @mixin set_bg( $color, $img, $w, $h ) {

    background-color: $color;
    background-image: url($img);
    background-size: $w $h;
    }

    // 引用
    div {
    @include set_bg( red, '../../images/1.jpg', $width, $height );
    }
  • 参数默认值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // test.scss
    @mixin set_bg( $color:green, $img, $w:100px, $h:100px ) {

    background-color: $color;
    background-image: url($img);
    background-size: $w $h;
    }

    // 引用
    div {
    @include set_bg( red, '../../images/1.jpg', $width, $height );
    }
如果设置了默认值,那么对应的参数可以不传,比如上例中的 `$width` 和 `$height` 就可以省略掉,在使用的时候可以直接写成: 

`@include set_bg( red, '../../images/1.jpg' );`

但是如果没有设置默认值,还省略的话就会报错:

![不设默认值省略参数时的报错](http://i.imgur.com/yyrlLNc.png)

因此在使用的时候务必要小心谨慎。
  • @content 使用

    在使用混合器时候,还可以结合 @content 来使用,这个时候不需要在选择器中使用 @include 就会生效,使用方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // test.scss

    @mixin test {
    * html {
    @content;
    }
    }

    @include test {
    #logo {
    background-color: red;
    }
    }
上面的结果就是:

1
2
3
4
// test.css
* html #logo {
background-color: red;
}
因此 `@content` 的使用可以考虑使用在全局的一些通用样式情况下。

4.5 继承

可以通过 @extend 来继承其他元素的样式

比如:

被继承元素:

1
2
3
.parent {
color: red;
}

需要继承的元素:

1
2
3
.son {
@extend .parent;
}

最后等价于 : .son { color: red; }

继承要点:

  1. 相对混合器代码量更少,因为继承是从选择器层面为出发点,而不是类似混合器直接复制属性;
  2. CSS 层叠问题,因为继承可能继承来自不同元素下的样式,比如 .a extend .b,那么也同样继承了 div.b 下的 .b 样式,那么此时就存在选择权的问题,也就是层叠问题,而这个时候选择的依据首先是权值,然后是声明顺序。

4.6 导入

sass 同样提供了样式文件导入的功能,同样使用 @import 关键词来使用,并且支持局部导入,即在某个选择器内部导入样式文件;

比如:

样式文件:

1
2
3
// a.scss

$bg_color: red;

在其他文件中导入 a.scss

1
2
3
4
5
// b.scss

div {
@import a;
}

最终生成结果其实就类似:

1
2
3
4
5
// b.scss

div {
$bg_color: red; // 在此声明了个局部变量
}

5 sass 语法扩展

5.1 占位符 + 继承

占位符,使用 % 来声明,通过占位符声明的样式块,可通过 @extend 来继承,并且占位符的使用也只有在被继承了之后才会生成,否则不会被编译到文件中去。

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// test.scss

%placeholder {
background-repeat: no-repeat;
}

@mixin set_bg( $color, $img, $w:60px, $h:60px ) {

background-color: $color;
background-image: url($img);
background-size: $w $h;

@extend %placeholder;
}

结果会发现:

1
2
3
4
5
6
7
div {
background-repeat: no-repeat; }

div {
background-color: red;
background-image: url("../../images/1.jpg");
background-size: 60px 60px; }

并没有和其他背景属性放在一个选择器中,而是单独复制了个选择器然后设置了占位符定义的属性,这也说明了继承并不是属性的复制而是选择器的复制。

5.2 函数

sass 中还可以使用 @function 来声明函数,并且 sass 提供了很多自带的功能函数,更详细的参考 第六节-函数 go ✈

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// test.scss

$width: 100px;
$stepW: 5px;

@function calcWidth( $w ) {
@return $width - $w * $stepW + $width / $stepW;
}

// 直接调用即可
div {
width: calcWidth( 10 ); // --> 结果为: 70px;
}

5.3 样式输出

scss 文件编译输出成 css 文件的时候可以通过参数来指定输出的样式版本

  • :nested 嵌套样式

    嵌套样式就是通过大括号,进行各种样式嵌套,使的样式代码清晰不至于混乱难以维护,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // test.css
    div {
    background-repeat: no-repeat; }

    div {
    background-color: red;
    background-image: url("../../images/1.jpg");
    background-size: 60px 60px; }

    .red {
    width: 70px;
    height: 100px;
    margin-top: 10px; }

    注意这里的成对的第二个大括号会和样式在同一行,这也是和后面一个输出样式不同的地方

  • :expended,其实就是 :nested 的展开模式

    1
    2
    3
    4
    // test.css
    div {
    background-repeat: no-repeat;
    } // 这个大括号单独成一行
  • :compact 紧凑型

    样式都写在一行上

    div { background-color: red; background-repeat: no-repeat; }

  • :compressed 压缩型

    去掉空格和换行后的形式,比如:

    1
    2
    // test.css
    div{background-repeat: no-repeat;}div{background-color:red;background-image:url("../../images/1.jpg");background-size:60px60px;}
压缩的形式相对更适合发布版本里面使用,可以一定程度上减少文件大小。

6 函数

Sass 提供了不少自带的工具函数,下面来简略的看下各个函数的功能

函数列表 地址下包含了 sass 提供的各种函数

6.1 颜色函数(Color Functions)

颜色函数主要分为三大类,包含(RGB,HSL,Opacity)和其他一些函数

6.1.1 RGB
  • rgb($red, $green, $blue):生成 rgb 颜色,参数值范围:0 ~ 255
  • rgb($red, $green, $blue, $opacity),同上,包含了透明度设置,透明度范围:0 ~ 1
  • red($color)
  • green($color)
  • blue($color)
  • mix($color1, $color2, [$weight]),混合两种颜色,$weight 表示混合比例,默认:50%,参数可以是 rgb/rgba 函数形式,也可以是 red/blue 颜色字符串形式

    混合示例:

    $bg-color: mix( red, blue, 30% );

    mix 颜色混合函数

6.1.2 HSL

HSL 是由:色相(hue),饱和度(saturation),亮度(lightness)三个值组合而成,三个值的取值范围分别为:

hue: 0 ~ 360 度
saturation: 0 ~ 100%
lightness: 0~ 100%

  • hsl($hue, $saturation, $lightness)
  • hsla($hue, $saturation, $lightness, $alpha)

    示例:

    hsl( 180, 30%, 60% )

    结果: #59a6a6;

    hsl/hsla

  • hue($color)

  • saturation($color)
  • lightness($color)

    上面三个均是从一个颜色值中取得相应的值

  • adjust-hue($color, $degrees)

  • lighten($color, $amount)
  • darken($color, $amount)
  • saturate($color, $amount)
  • desaturate($color, $amount)

    上面的5个函数都是在调整 $color 颜色,最后返回一个调整后的颜色值

  • grayscale($color)

  • complement($color)
  • invert($color)
6.1.3 Opacity
  • alpha($color) / opacity($color):获取透明度
  • rgba($color, $alpha):设置透明度
  • opacify($color, $amount) / fade-in($color, $amount):不透明化,相当于淡入
  • transparentize($color, $amount) / fade-out($color, $amount):透明化,相当于淡出
6.1.4 其他
  • adjust-color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])

    设置之前:rgba(204, 204, 204, 0.7);

    1
    2
    3
    4
    5
    6
    $bg-color: adjust-color(
    $bg-color,
    $red:-10, $green:20, $blue:-20,
    // $hue:180, $saturation:30%, $lightness:20%,
    $alpha:0.3
    );

    设置之后:#c2e0b8; ==> rgb(194,224,184);

    从结果上看,该函数是在原有的基础上进行加减得来,参数是负值就是减,正值就是加;

  • scale-color($color, [$red], [$green], [$blue], [$saturation], [$lightness], [$alpha])

  • change-color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])

    以上三个函数都可以对某个颜色值进行修改,修改的内容涉及 RGBHSL 六个值;

    在使用过程中遇到两个问题:

    1. 直接传入数值会报错,原因在于,传值必须以:$red:30 形式传入;
    2. 报错:不能对一个颜色同时设置 HSL 和 RGB;
  • ie-hex-str($color):将颜色转换成 IE 可识别的 16 进制字符串;

1
2
3
ie-hex-str(#abc) => #FFAABBCC
ie-hex-str(#3322BB) => #FF3322BB
ie-hex-str(rgba(0, 255, 0, 0.5)) => #8000FF00

6.2 字符串处理函数(String Functions)

  • unquote($string)
  • quote($string)

    去掉引号和添加引号

  • str-length($string):获取字符串长度

  • str-insert($string, $insert, $index),将 $insert 插入到字符串的 $index 位置;
  • str-index($string, $substring):查找子串,返回索引;
  • str-slice($string, $start-at, [$end-at]):切割字符串
  • to-upper-case($string):大写化
  • to-lower-case($string):小写化

这些字符串操作函数,

6.3 数字处理函数(Number Functions)

  • percentage($number):将不带参数的数字转换成百分比

    比如:

    percentage( 100 ); => 10000%

    percentage( 0.8 ); => 80%

  • round($number):四舍五入

  • ceil($number):向上取整
  • floor($number):向上取整
  • min($numbers…): 取最小值
  • max($numbers…):取最大值
  • random([$limit]):取随机数,从 1 ~ $limit 之间的随机数

    设置个随机背景色:

    $bg-color: rgba( random(255), random(255), random(255), random(100) / 100 );

    其实编译之后还是个固定值,还在傻傻的一个劲刷新浏览器(囧囧囧囧!!!!)

6.4 列表处理函数(List Functions)

sass 中的所有列表都是不可变的,所提供的列表函数在操作列表之后都是返回新的列表,原来列表不会发生变化;

sass 中的列表,有几种表现形式:

  1. 空格型:

    比如(border: 1px solid red)中的 1px solid red 就可以定义为一个列表,

    $list: 1px solid red;

  2. 逗号型:

    比如(rgb(120, 120, 120);)中的 120, 120, 120 就可以这样定义

    $list: 120, 120, 120;

函数列表:

  • length($list)

    示例:

    1
    2
    3
    4
    $list: 120, 120, 120;
    $list: 120 120 120;

    $bg-color: rgba( 120, 120, 120, length($list) / 10 );
结果: `background-color: rgba(120, 120, 120, 0.3);` 得到 `$list` 长度:3
  • nth($list, $n)

    示例:

    1
    2
    3
    4

    $list: 1, 2, 3, 4, 5, 6;

    $bg-color: rgba( 120, 120, 120, nth($list, 4) / 10 );
结果: `background-color: rgba(120, 120, 120, 0.4);`,从结果可知通过 `nth` 取得了 `$list` 中的第四个元素也就是:`4`。
  • set-nth($list, $n, $value)

    示例:

    1
    2
    3
    4
    5
    $list: 1, 2, 3, 4, 5, 6;

    $list: set-nth($list, 5, 9);

    $bg-color: rgba( 120, 120, 120, nth($list, 5) / 10 );
结果:`background-color: rgba(120, 120, 120, 0.9);`

从结果可知,通过 `set-nth($list, 5, 9)` 把 `$list` 中的第五个值变成了 `9`,但是上面提过,所有的列表操作函数都无法改变原列表,所以,在设置完之后,必须的重新赋值给 `$list` ,也就是上面示例中的第二行代码必须要有;
  • join($list1, $list2, [$separator])
  • append($list1, $val, [$separator])

    上面两个也挺简单,就不多说了,join 就是合并列表,append 就是在列表后面追加值;

  • zip($lists…)

    该函数是将多个一维列表组合成一个多维列表,从官网的示例看,组合方式需要多注意。

    1
    2
    zip(1px 1px 3px, solid dashed solid, red green blue)
    => 1px solid red, 1px dashed green, 3px solid blue
  • list-separator($list): 返回列表的分隔符

6.5 映射处理函数(Map Functions)

  • map-get($map, $key):根据给出的 $key 得到相应的值;
  • map-merge($map1, $map2):合并两个图;
  • map-remove($map, $keys…):删除指定键的项;
  • map-keys($map):得到所有键;
  • map-values($map):得到所有值;
  • map-has-key($map, $key):判断是否存在给出的键的项;
  • keywords($args):类似于字符串化参数似得。

    比如:

    1
    2
    3
    4
    5
    @mixin foo( args... ) {
    @debug keywords( $args ); // => (arg1:v1, arg2:v2)
    }

    @include($arg1:v1, $arg2:v2);

6.6 选择器函数(Selector Functions)

  • selector-nest($selectors…)

    子元素选择器处理:会在后面加上空格,再链接起来,如果后面的参数中包含父元素的引用:&,那么不使用空格而是直接链接到前面选择器中;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 无 ‘&’ 情况,空格连接
    selector-nest( ".a .b", ".c .d" ); // .a .b .c .d

    // 如果是下面的有逗号的情况,那么就是在 .a 和 .b 下面各自添加 .c .d
    selector-nest( ".a, .b", ".c .d" ); // .a .c .d, .b .c .d


    // 有 ‘&’ 情况,直接连接
    selector-nest( ".a", "&.b" ); // .a.b
  • selector-append($selectors…):往前面追加,中间不添加分隔符

    比如:

    1
    2
    3
    selector-append( ".a", ".b", ".c" );    // ==> .a.b.c
    selector-append( ".a.foo", ".b.bar"); // ==> .a.foo.b.bar
    selector-append( ".a", "-suffix" ); // ==> .a-suffix
  • selector-extend($selector, $extendee, $extender)

    $extendee:被继承者;
    $extender:继承者;

    官方的解释是,如同下面的形式:

    1
    2
    $selector { ... }
    $extender { @extend $extendee }
从示例看也还是没太明白意思:

`selector-extend(".a .b", ".b", ".foo .bar") => .a .b, .a .foo .bar, .foo .a .bar`

`.a .b, .a .foo .bar` 这两个还好理解,之后后面一个 :`.foo .a .bar` 不太理解,怎么 `.foo` 又成了父级选择器了。
  • selector-replace($selector, $original, $replacement)

    这个函数作用是用来进行选择器替换用的,支持替换全部或者部分

    官方示例:

    1
    2
    selector-replace(".foo .bar", ".bar", ".baz") => ".foo .baz"
    selector-replace(".foo.bar.baz", ".foo.baz", ".qux") => ".bar.qux"
从示例看可知,`replace` 还是很强大的, `.foo.bar.baz` 在 查找 `.foo.baz` 情况下都能替换掉,变成 `.bar.qux`

[这篇文章](http://www.w3cplus.com/preprocessor/Sass-3-3-new-feature-at-root-bem.html)讲述了 `@at-root` 有关的使用,值得一看。
  • selector-unify($selector1, $selector2)
  • is-superselector($super, $sub)$super 选择器是否被 $sub 包含
  • simple-selectors($selector): 将选择器组合分割成简单的选择器,返回分割后的选择器列表;例如:simple-selectors(".foo.bar.baz"); // => ".foo",".bar",".baz"
  • selector-parse($selector): selector-parse(".foo .bar, .baz .bang") => ('.foo' '.bar', '.baz' '.bang'),解析给出的选择器字符串成列表;

6.7 内部函数(Introspection Functions)

  • feature-exists($feature)
  • variable-exists($name)
  • global-variable-exists($name)
  • function-exists($name)
  • mixin-exists($name)
  • inspect($value)
  • type-of($value)
  • unit($number)
  • unitless($number)
  • comparable($number1, $number2)
  • call($name, $args…)

上面都是一些基本的内部函数,大部分都可以直观的通过函数名来识别其用途。需要注意的几点

  1. unit 返回的是一个数的单位;而 unitless 是返回 boolean 值用来判断数字是否带有单位;
  2. comparable:用来判断两个数是否能进行加减比较操作,比如在不同单位的情况下可能无法做出算术操作,那么就可以事先通过这个去判断下;

6.8 其他函数(Miscellaneous Functions)

  • if($condition, $if-true, $if-false):这个函数类似与三元操作符,表示,如果条件 $condition 成立,就执行$if-true,否则执行$if-false
  • unique-id():生成唯一的 CSS id,不带引号形式返回;

更多与函数有关的知识可以在官方文档-函数部分获取

7 其他

7.1 插值

在属性名当中相同的部分使用变量是使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// test.scss

$props: (margin, padding);

@mixin set_top( $direction, $value ) {
@each $prop in $props {
#{$prop}-#{$direction}: $value;
}
}


.red {
width: set_width( 10 );
height: $height;
margin-top: $mtop;
@include set_top( left, 100px );
}

上面的 $props 的声明类型,是 sass 中的数组,包含两个元素: marginpadding,
插值的引用,使用 #{} 方式,经常被用在属性名上

7.2 各种指令

指令的使用采用 @name 形式,相当于 sass 中的关键字。

@each: 遍历数组用,类似 for ... in
@if@else@else if@for 等等都可以在 sass 中使用

  • @for

    @for $i from 1 through 3 { .item-#{$i} { width: 100px; } };

  • @each

    @each $prop in $props { .item-#{$prop} { ... } }

  • @while

    @while $i > 0 { ... }

7.3 不定参数(…)

在参数比较多,或者不确定的时候,可以使用 ...

比如:

1
2
3
4
5
6
7
8
9
10
// test.scss
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}

.shadows {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}

传值的时候同样可以使用:

1
2
3
4
5
6
7
8
9
10
11
12
// test.scss

$values: 11, 22, 33, 44;

@mixin test( $v1, $v2, $v3, $v4 ) {
......
}

div {

@include test( $values... ); // 这里意思是把 $values 中的所有值传入到混合器中
}

7.4 运算符

  • 算术: 加减乘除(+ - * /);
  • 逻辑: 或(or),非(not),且(and);

8 总结

SASS 的基本使用还是比较简单的,基本上只要记住以下几点,入门还是很快的

  1. 变量声明使用‘$’,比如:$result
  2. 全局和局部变量声明;
  3. 关键字部分,使用 @,比如:@mixin@extend@include@content等等;
  4. 控制语句:@for, @if, @each, @else if, @while 等的使用;
  5. 混合器,继承,占位符的使用;
  6. 运算符(算术(+ - * /),逻辑(orandnot),等等);
  7. 文件引入:@import 这里需要注意的是针对原生Css文件和Sass/scss文件引入的注意点;
  8. 函数声明:@function
  9. 各种函数的使用;
  10. &@at-root 的结合使用。
0%