SASS基础

Sass是一门高于 CSS的元语言,它能用来清晰地、结构化地描述文件样式,有着比普通 CSS更加强大的功能。Sass能够提供更简洁、更优雅的语法,同时提供多种功能来创建可维护和管理的样式表。

理论

什么是 CSS预处理器

通俗的说,“CSS预处理器用一种专门的编程语言,进行 Web页面样式设计,然后再编译成正常的 CSS文件,以供项目使用。CSS预处理器为 CSS增加一些编程的特性,无需考虑浏览器的兼容性问题”,例如你可以在 CSS中使用变量、简单的逻辑程序、函数等在编程语言中的一些基本特性,可以让你的 CSS更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。

有哪些CSS预处理器语言

CSS 预处理器技术已经非常的成熟,而且也涌现出了很多种不同的 CSS 预处理器语言,比如说:

  • Sass(SCSS)
  • LESS
  • Stylus
  • Turbine
  • Swithch CSS
  • CSS Cacheer
  • DT CSS

就目前而言,应用最多的有Sass、LESS、Stylus。

Sass与SCSS的区别

Sass和 SCSS其实是同一种东西,我们平时都称之为 Sass,两者之间不同之处有以下两点:

  • 文件扩展名不同,Sass是以“.sass”后缀为扩展名,而 SCSS是以“.scss”后缀为扩展名。
  • 语法书写方式不同,Sass是以严格的缩进式语法规则来书写,不带大括号({})和分号(;),而 SCSS的语法书写和我们的 CSS语法书写方式非常类似。

Sass语法

1
2
3
4
5
6
$font-stack: Helvetica, sans-serif //定义变量
$primary-color: #333 //定义变量
body
font: 100% $font-stack
color: $primary-color

SCSS语法

1
2
3
4
5
6
7
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}

编译出来的CSS

1
2
3
4
body {
font: 100% Helvetica, sans-serif;
color: #333;
}

基本用法

变量

声明变量
1
2
3
4
5
6
7
8
$width: 300px;
$ //变量声明符“$”
width //变量名称
300px //赋予变量的值
注意:变量的声明不区分中划线和下划线
例如:$h_color和$h-color是同一个变量
变量的调用与插值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$width: 300px;
$top: top;
.box{
width: $width
border-#{$top}: 1px solid red;
#{$top}:20px;
}
//编译后
.box {
width: 100px;
border-top: 1px solid red;
top: 20px;
}
注意:如果变量需要镶嵌在字符串之中,就必须需要写在#{}之中。
普通变量与默认变量
1
2
3
4
5
6
7
$fontSize: 12px; //普通变量,可以在全局范围内使用
$baseLineHeight:1.5 !default; //默认变量,仅需要在值后面加上 !default 即可
body{
font-size:$fontSize; //font-size:12px;
line-height: $baseLineHeight; //line-height:1.5;
}
默认变量的意义
1
2
3
4
5
6
$baseLineHeight: 2;
$baseLineHeight: 1.5 !default;
body{
line-height: $baseLineHeight; //line-height:2;
}

默认变量一般是用来设置默认值,然后根据需求来覆盖,覆盖的方式也很简单,只需要在默认变量之前重新声明下变量即可。默认变量的价值在进行组件化开发的时候会非常有用。

局部变量和全局变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//SCSS
$color: orange !default;//定义全局变量(在选择器、函数、混合宏...的外面定义的变量为全局变量)
.block {
color: $color;//调用全局变量
}
em {
$color: red;//定义局部变量
a {
color: $color;//调用局部变量
}
}
span {
color: $color;//调用全局变量
}
//CSS
.block {
color: orange;
}
em a {
color: red;
}
span {
color: orange;
}

上面的代码可以得知,在元素内部定义的变量不会影响其它元素。如此可以简单的理解成,全局变量就是定义在元素外面的变量。

全局变量的影子

当在局部范围(选择器内、函数内、混合宏内…)声明一个已经存在于全局范围内的变量时,局部变量就成为了全局变量的影子。基本上,局部变量只会在局部范围内覆盖全局变量。

什么时候声明变量

创建变量只适用于感觉的确有必要的情况下。不要为了声明变量而声明变量,这丝毫没有作用。只有满足所有下述标准时方可创建新变量:

  • 该值至少重复出现了两次;
  • 该值至少可能会被更新一次;
  • 该值所有的表现都与变量有关(非巧合)。

基本上,没有理由声明一个永远不需要更新或者只在单一地方使用变量。

数据类型

Sass 和 JavaScript语言类似,也具有自己的数据类型

1
2
3
4
5
6
数字: 如,1、 2、 13、 10px
字符串:有引号字符串或无引号字符串,如,"foo"、 'bar'、 baz
颜色:如,blue、 #04a3f9、 rgba(255,0,0,0.5)
布尔型:如,true、 false
空值:如,null
值列表:用空格或者逗号分开,如,1.5em 1em 0 2em 、 Helvetica, Arial, sans-serif

SassScript也支持其他 CSS属性值(property value),比如 Unicode范围,或 !important声明。然而Sass不会特殊对待这些属性值,一律视为无引号字符串 (unquoted strings)

字符串

SassScript支持 CSS的两种字符串类型

1
2
有引号字符串 (quoted strings),如 "Lucida Grande" 、'http://sass-lang.com'
无引号字符串 (unquoted strings),如 sans-serifbold

在编译 CSS文件时不会改变其类型。只有使用 #{ }插值语句 (interpolation)时,有引号字符串将被编译为无引号字符串,这样方便了在混合指令 (mixin) 中引用选择器名

1
2
3
4
5
6
7
8
9
10
//SCSS
@mixin firefox-message($selector) {
body.firefox #{$selector}:before {
content: "Hi, Firefox users!";
}
}
@include firefox-message(".header");
//CSS
body.firefox .header:before { content: "Hi, Firefox users!"; }

注释

在 Sass中注释有两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//SCSS
//这是注释
%mt5 {
margin-top: 5px;
}
/*这也是注释*/
.box {
@extend %mt5;
}
//CSS
.box {
margin-top: 5px;
}
/*这也是注释*/

嵌套

Sass的嵌套分为三种:

  • 选择器嵌套
  • 属性嵌套
  • 伪类嵌套
选择器嵌套
1
2
3
4
& 父级选择器
> 子级选择器
+ 相邻选择器
~ 同级选择器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//SCSS
article {
~ article { border-top: 1px dashed #ccc }
> section { background: #eee }
dl > {
dt { color: #333 }
dd { color: #555 }
}
nav + & { margin-top:0;}
&:hover{color:red;}
}
//CSS
article ~ article { border-top: 1px dashed #ccc }
article > section { background: #eee }
article dl > dt { color: #333 }
article dl > dd { color: #555 }
nav + article { margin-top: 0 }
article :hover{color:red;}
属性嵌套

CSS有一些属性前缀相同,只是后缀不一样,比如:border-top/border-right,与这个类似的还有 margin、padding、font 等属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
//CSS
.box {
border-top: 1px solid red;
border-bottom: 1px solid green;
}
//SCSS
.box {
border: {
top: 1px solid red;
bottom: 1px solid green;
}
}

伪类嵌套

其实伪类嵌套和属性嵌套非常类似,只不过他需要借助 & 符号一起配合使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//SCSS
.clearfix{
&:before,
&:after {
content:"";
display: table;
}
&:after {
clear:both;
overflow: hidden;
}
}
//CSS
clearfix:before, .clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
overflow: hidden;
}

混合宏 /继承 /占位符

各自优缺点及使用
名称 优点 缺点 使用
混合宏 可以传参数 不会自动合并相同的样式代码 如果你的代码块中涉及到变量,建议使用混合宏
继承 继承选择器定义样式的基类,不需要单独定义。可以合并相同的代码 不能传参,不管调用或不调用都会编译 如果你的代码块不需要专任何变量参数,而且有一个基类已在文件中存在
占位符 基类不会被编译 需要单独定义基类
总结
混合宏 继承 占位符
声明方式 @mixin .class %placeholder
调用方式 @include @extend @extend
使用环境 如果相同代码块需要在不同的环境传递不同的值时,可以使用混合宏来定义重复使用的代码块 如果相同的代码块不需要传递不同的值,并且此代码块已在SCSS中定义,可以通过SCSS的继承来调用已存在的基类。使用继承将会将调用相同代码的基类合并在一起 占位和继承基本类似,唯一不同的是,相同代码块并没有在基类中存在,而是额外声明。如果不调用已声明的占位符,将不会产生任何样式代码,如果在不同的选择器调用占位符,那么编译出来的CSS将会把相同的代码合并在一起
不足 编译出来的CSS代码多次出现调用的混合宏对应代码块,使文件变得臃肿,代码冗余 如果基类并不存在与HTML结构时,不管调用不调用,在编译出来的CSS中都将产生基类对应的样式代码
混合宏
声明

使用@mixin来声明一个混合宏

1
2
3
4
@mixin border{
border: 1px solid red;
border-radius: 5px;
}

调用

通过@include来调用声明好的混合宏

1
2
3
.box{
@include border;
}

传参

参数用于给混合指令中的样式设定变量,并且赋值使用。在定义混合指令的时候,按照变量的格式,通过逗号分隔,将参数写进圆括号里。引用指令时,按照参数的顺序,再将所赋的值对应写进

1
2
3
4
5
6
7
8
@mixin sexy-border($color, $width) {
border: {
color: $color;
width: $width;
style: solid;
}
}
p { @include sexy-border(blue, 1px); }

混合指令也可以使用给变量赋值的方法给参数设定默认值,然后,当这个指令被引用的时候,如果没有给参数赋值,则自动使用默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//SCSS
@mixin sexy-border($color, $width: 1px) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
p { @include sexy-border(red); }
h1 { @include sexy-border(red, 2px); }
//CSS
p {
border-color: red;
border-width: 1px;
border-style: solid;
}
h1 {
border-color: red;
border-width: 2px;
border-style: solid;
}

关键词参数

混合指令也可以使用关键词参数
注意:关键词参数不需要按照参数顺序传参,可以打乱顺序使用

1
2
3
4
5
6
7
8
9
p {
@include sexy-border($color: blue);
}
h1 {
@include sexy-border($color: blue, $width: 2px);
}
h2 {
@include sexy-border($width: 2px , $color: blue);
}

传参(不确定个数)

有时,不能确定混合指令需要使用多少个参数,比如一个关于 box-shadow 的混合指令不能确定有多少个 shadow 会被用到。这时,可以使用参数变量 … 声明(写在参数的最后方)告诉 Sass 将这些参数视为值列表处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//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);
}
//CSS
.shadowed {
-moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
-webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
}

参数变量也可以用在引用混合指令的时候 (@include),与平时用法一样,将一串值列表中的值逐条作为参数引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//SCSS
@mixin colors($text, $background, $border) {
color: $text;
background-color: $background;
border-color: $border;
}
$values: #ff0000, #00ff00, #0000ff;
.primary {
@include colors($values...);
}
//CSS
.primary {
color: #ff0000;
background-color: #00ff00;
border-color: #0000ff;
}

继承

在 Sass中是通过关键词 @extend来继承已存在的类样式块,从而实现代码的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//SCSS
.btn {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
.btn-primary {
background-color: #f36;
color: #fff;
@extend .btn;
}
.btn-second {
background-color: orange;
color: #fff;
@extend .btn;
}
//CSS
.btn, .btn-primary, .btn-second {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
.btn-primary {
background-color: #f36;
color: #fff;
}
.btn-second {
background-clor: orange;
color: #fff;
}

占位符

Sass中的占位符 %功能是一个很强大,很实用的一个功能。它可以取代以前 CSS中的基类造成的代码冗余的情形。因为 %placeholder声明的代码,如果不被 @extend调用的话,不会产生任何代码

1
2
3
4
5
6
%mt5 {
margin-top: 5px;
}
%pt5{
padding-top: 5px;
}

这段代码没有被 @extend调用,它并没有产生任何代码块,只是静静的躺在某个 SCSS文件中,只有通过 @extend调用才会产生代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//SCSS
%mt5 {
margin-top: 5px;
}
%pt5{
padding-top: 5px;
}
.btn {
@extend %mt5;
@extend %pt5;
}
.block {
@extend %mt5;
span {
@extend %pt5;
}
}
//CSS
.btn, .block {
margin-top: 5px;
}
.btn, .block span {
padding-top: 5px;
}

运算

加法
1
2
3
4
5
6
7
8
9
10
//SCSS
$one: 100px;
$two: 200px;
.box { width: $one + $two; }
//CSS
.box { width: 300px; }
//注意:携带不同类型的单位时,在 SCSS中计算会报错
.box { width: 20px + 1em; } //报错
减法
1
2
3
4
5
6
7
8
9
10
//SCSS
$one: 100px;
$two: 200px;
.box { width: $two - $one; }
//CSS
.box { width: 100px; }
//注意:携带不同类型的单位时,在 SCSS中计算会报错
.box { width: 20px - 1em; } //报错
乘法
1
2
3
4
5
6
7
8
9
//SCSS
.box { width:10px * 2px;} //报错,进行乘法运算时,两个值单位相同时,只需要为一个数值提供单位即可
.box { width: 10px * 2; } //正确
//CSS
.box { width: 20px; }
//注意:携带不同类型的单位时,在 SCSS中计算会报错
.box { width: 20px * 1em; } //报错
除法

除法运算/在 CSS中通常起到分隔数字的用途,SassScript作为 CSS语言的拓展也支持这个功能,同时也赋予了/除法运算的功能。如果 /在 SassScript中把两个数字分隔,编译后的 CSS文件中也是同样的作用

以下三种情况 / 将被视为除法运算符号:

  • 如果数值或它的任意部分是存储在一个变量中或是函数的返回值
  • 如果数值被圆括号包裹
  • 如果数值是算数表达式的一部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//SCSS
$width: 1000px;
p {
font: 10px/8px; // 纯 CSS,不是除法运算
width: $width/2; // 使用了变量,是除法运算
width: round(1.5)/2; // 使用了函数,是除法运算
height: (500px/2); // 使用了圆括号,是除法运算
margin-left: 5px + 8px/2px; // 使用了加(+)号,是除法运算
}
//CSS
p {
font: 10px/8px;
width: 500px;
height: 250px;
margin-left: 9px;
}
数字运算
1
2
3
4
5
6
7
//SCSS
.box {
width: ((220px + 720px) - 11 * 20 ) / 12 ;
}
//CSS
.box { width: 60px; }
颜色值运算
1
2
3
4
5
p {color: #010203 + #040506;}
计算公式为 01 + 04 = 05、02 + 05 = 07 和 03 + 06 = 09
p { color: #050709; }
1
2
3
4
5
p { color: #010203 * 2; }
计算公式为 01 * 2 = 02、02 * 2 = 04 和 03 * 2 = 06
p { color: #020406; }
字符运算

在 Sass 中可以通过加法符号“+”来对字符串进行连接

1
2
3
4
5
6
7
8
//SCSS
$content: "Hello" + "" + "Sass!";
.box:before {
content: " #{$content} ";
}
//CSS
.box:before { content: " Hello Sass! "; }

除了在变量中做字符连接运算之外,还可以直接通过 +,把字符连接在一起

1
2
3
4
5
//SCSS
div { cursor: e + -resize; }
//CSS
div { cursor: e-resize; }

注意:如果有引号的字符串被添加了一个没有引号的字符串 (也就是,带引号的字符串在 + 符号左侧), 结果会是一个有引号的字符串。 同样的,如果一个没有引号的字符串被添加了一个有引号的字符串 (没有引号的字符串在 + 符号左侧), 结果将是一个没有引号的字符串

1
2
3
4
5
6
7
8
9
10
11
//SCSS
p:before {
content: "Foo " + Bar;
font-family: sans- + "serif";
}
//CSS
p:before {
content: "Foo Bar";
font-family: sans-serif;
}