Sass基础及快速入门

1899 4 年前
Sass 是对 CSS 的扩展,让 CSS 语言更强大、优雅。 它允许你使用变量、嵌套规则、 mixins、导入等众多功能, 并且完全兼容 CSS 语法。 Sass 有助于保持大型样式表结构良好, 同时也让你能够快速开始小型项目, 特别是在搭配 Compass 样式库一同使用时。

使用变量

sass使用 $ 符号来标识变量

声明变量

$highlight-color:#f90

$basic-border: 1px solid black;

变量可以在css规则块外声明也可以在其内声明,在css规则块内声明的变量只能在此规则块内使用。

$nav-color: #F90;
nav {
  $width: 100px;
  width: $width;
  color: $nav-color;
}

//编译后

nav {
  width: 100px;
  color: #F90;
}

在声明变量时,变量值也可以引用其他变量。

$highlight-color: #F90;
$highlight-border: 1px solid $highlight-color;
.selected {
  border: $highlight-border;
}

//编译后

.selected {
  border: 1px solid #F90;
}

变量名称 中划线 / 下划线

sass的变量名可以与css中的属性名和选择器名称相同,包括中划线和下划线。

$highlight-color $highlight_color使用中划线的方式更为普遍,这也是compass和本文都用的方式。不过,sass并不想强迫任何人一定使用中划线或下划线,所以这两种用法相互兼容。用中划线声明的变量可以使用下划线的方式引用,反之亦然。

$link-color: blue;
a {
  color: $link_color;
  
}

//编译后

a {
  color: blue;
}

sass的大 多数地方,中划线命名的内容和下划线命名的内容是互通的,除了变量还包括混合器Sass函数的命名。但是在sass中纯css部分不互通(border-bottom: #1da1f2;),比如类名、ID或属性名。

引用变量

凡是css属性的标准值(比如说1px或者bold)可存在的地方,都可以变量。

$highlight-color: #F90;
.selected {
  border: 1px solid $highlight-color;
}

//编译后

.selected {
  border: 1px solid #F90;
}

嵌套CSS 规则

css中重复写选择器是非常恼人的,以前我们需要一遍一遍地写层级。

#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }

sass里你只需写一遍,在规则块中嵌套规则块即可,在一个选择器块中可以包含其子元素的选择器。

#content {
  article {
    h1 { color: #333 }
    p { margin-bottom: 1.4em }
  }
  aside { background-color: #EEE }
}

 /* 编译后 */
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }

表示,在ID为content的元素下面的articleaside元素的样式。

一个给定的规则块,既可以像普通的CSS那样包含属性,又可以嵌套其他规则块。

#content {
  background-color: #f5f5f5;
  aside { background-color: #eee }
}

//编译后

#content { background-color: #f5f5f5 }
#content aside { background-color: #eee }

父选择器的标识符&

前面我们讲的嵌套其实是CSS里边被称为后代选择器,sass在解开一个嵌套规则时就会把父选择器(#content)通过一个空格连接到子选择器的前边(articleaside)形成(#content article#content aside),但有些情况,我们不希望使用这种后代选择器的方式生成这种连接。

比如:hover这种伪类

article a {
  color: blue;
  :hover { color: red }
}
<article>
	<a href="/">
		<span>内容一</span>
		<span>内容二</span>
	</a>
</article>

比如上面的代码,编译连接后为article a :hover{ color: red }表示article a下面的所有元素,当鼠标放上去的时候文字变为红色,鼠标在不同的span时只有这个span的文字变为了红色。而我们需要的是连接后为article a:hover{ color: red }的效果,鼠标放在这个a上,其下的所有文字变为红色。

为此,sass提供了一种特殊的选择器:& 即父选择器。

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

//编译后
article a { color: blue }
article a:hover { color: red }

在为父级选择器添加:hover等伪类时,这种方式非常有用。同时父选择器标识符还有另外一种用法,你可以在父选择器之前添加选择器。

.content aside {
  color: red;
  body.mybody & { color: green }
}

/*编译后*/
.content aside {color: red};
body.mybody .content aside { color: green }

群组选择器的嵌套

CSS里边,选择器h1,h2,h3会同时命中h1元素、h2元素和h3元素。.button,button会命中button元素和类名为.button的元素。这种选择器称为群组选择器

.button, button {
  margin: 0;
}

以前,如果我们要对一个元素下某些元素进行修饰,不得不重复写选择器:

.container h1, .container h2, .container h3 { margin-bottom: .8em }

然而在sass里,你可以在嵌套规则里使用群组选择器来达到这样的效果:

.container{
	h1, h2, h3 {margin-bottom: .8em}
}

对于嵌套在群组选择器内的嵌套规则:

nav, aside {
  a {color: blue}
}

sassnavaaside分别与a组合,然后将二者重新组合成一个群组选择器:

//nav a {color: blue}
//aside a {color: blue}

//重新组合
nav a, aside a {color: blue}

有利必有弊,要特别注意群组选择器的规则嵌套生成的css。虽然sass让你的样式表看上去很小,但实际生成css可能非常大。

关于选择器嵌套的最后一个方面,我们看看sass如何处理组合选择器,比如>+~的使用。你将看到,这种场景下你甚至无需使用父选择器标识符。

子组合选择器和同层组合选择器:>、+、~;

一个css选择器的例子:

article section { margin: 5px }
article > section { border: 1px solid #ccc }

第一行是一个包含选择符,即包含在article元素里的所有section,子子孙孙;

第二行为子选择符,即包含在article元素里的所有section子元素,儿子一代。

>+~分别表示子选择符相邻选择符兄弟选择符

header + p { font-size: 1.1em }

表示紧贴在header元素之后p元素

header ~ p { font-size: 1.1em }

表示选择跟header元素同层的所有兄弟元素p

上面这些组合选择器可以毫不费力地应用到sass的规则嵌套中。

article {
  ~ article { border-top: 1px dashed #ccc }
  > section { background: #eee }
  dl > {
    dt { color: #333 }
    dd { color: #555 }
  }
  nav + & { margin-top: 0 }
}

//编译后

article ~ article { border-top: 1px dashed #ccc }
article > footer { background: #eee }
article dl > dt { color: #333 }
article dl > dd { color: #555 }
nav + article { margin-top: 0 }

嵌套属性

sass中,除了CSS选择器,属性也可以进行嵌套,例如你只需敲写一遍border从而实现border-styleborder-widthborder-color的定义。

nav {
  border: {
  style: solid;
  width: 1px;
  color: #ccc;
  }
}

嵌套属性的规则是:把属性名从中划线 - 的地方断开,在根属性后边添加一个冒号 : ,紧跟一个 { } 块,把子属性部分写在这个 { } 块中。就像css选择器嵌套一样,sass会把你的子属性一一解开,把根属性和子属性部分通过中划线 - 连接起来,最后生成的效果与你手动一遍遍写的css样式一样:

nav {
  border-style: solid;
  border-width: 1px;
  border-color: #ccc;
}

对于缩写形式,你甚至可以像下边这样来嵌套,指明例外规则:

nav {
  border: 1px solid #ccc {
  left: 0px;
  right: 0px;
  }
}

//编译后

nav {
  border: 1px solid #ccc;
  border-left: 0px;
  border-right: 0px;
}

导入SASS文件

处理大量样式的唯一方法就是把它们分拆到多个文件中。sass通过对css原有@import规则的改进直接支持了这一特性。@import它允许在一个css文件中导入其他css文件

css@import规则,它允许在一个css文件中导入其他css文件。只有执行到@import时,浏览器才会去下载其他css文件,这导致页面加载起来特别慢。

sass@import规则是在生成css文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个css文件中,而无需发起额外的下载请求。另外,所有在被导入文件中定义的变量和混合器均可在导入文件中使用。

sass@import不需要指明被导入文件的全名。你可以省略.sass.scss文件后缀

使用SASS部分文件

当通过@importsass样式分散到多个文件时,你通常只希望生成少数几个css文件。那些专门为@import命令而编写的sass文件,并不需要生成对应的独立css文件,这样的sass文件称为局部文件。对此,sass有一个特殊的约定来命名这些文件,文件名以下划线( _ )开头)(_buttons.scss),这样,sass就不会在编译时单独编译这个文件输出css。当你@import一个局部文件时,还可以省略文件名开头的下划线。例如想导入themes/_night-sky.scss这个局部文件只需在样式表中写@import "themes/night-sky"

默认变量值

局部文件可以被多个不同的文件引用,一个文件也可以引入多个局部文件,这有可能会导航变量重复声明,最后一处声明有效且它会覆盖前边的值。

$link-color: blue;
$link-color: red;
a {
	color: $link-color;
}

上面的链接的color会被设置为red,然而,有时你希望别人@import导入你的sass库文件可以定制某些值,这种情况,你可以声明变量时使用!default标签,含义是:如果这个变量被声明赋值了,那就用它声明的值,否则就用这个默认值。

$fancybox-width: 400px !default;
.fancybox {
	width: $fancybox-width;
}

如果用户在导入你的sass局部文件之前声明了一个$fancybox-width变量,那么你的局部文件中对$fancybox-width赋值400px的操作就无效;如果用户没有做这样的声明,则$fancybox-width将默认为400px

嵌套导入

sass允许@import命令写在css规则内,而在生成css文件时,局部文件会被直接插入到导入它的css规则的地方。

例如:

//_blue-theme.scss

aside {
  background: blue;
  color: white;
}

然后导入到一个CSS规则内:

.blue-theme {
	@import "blue-theme"
}

//生成的结果跟你直接在.blue-theme选择器内写_blue-theme.scss文件的内容完全一样。

/*.blue-theme {
  aside {
    background: blue;
    color: #fff;
  }
}
*/

.blue-theme aside {
	background: blue;
    color: #fff;
}

被导入的局部文件中如果定义了变量和混合器,那么他们也只会在这个规则范围内生效。

原生的CSS导入

sass也支持导入原生的css文件,通常在sass中使用@import时,sass会尝试找到对应的sass文件并导入进来,但在下列三种情况下会生成原生的CSS @import,造成浏览器解析css时要额外下载:

  1. 被导入文件的名字以.css结尾;
  2. 被导入文件的名字是一个URL地址(比如http://www.tensweets.com/pc/style/main.css);
  3. 被导入文件的名字是CSSurl()值,@import url(CSS文件路径地址)

sass的语法完全兼容css,所以你可以把原始的.css文件改名为.scss后缀,即可直接导入了。

静默注释

sass另外提供了一种不同于css标准注释格式 /* ... */ 的注释语法,即静默注释,其注释部分的内容不会出现在生成的css文件中,//开头,注释内容直到行末。

body {
  color: #333; // 这种注释内容不会出现在生成的css文件中
  padding: 0; /* 这种注释内容会出现在生成的css文件中 */
}

css的标准注释格式/* ... */内的注释内容亦可在生成的css文件中抹去。当注释出现在原生css不允许的地方,sass将不知如何将其生成到对应css文件中的相应位置,于是这些注释被抹掉。

body {
  color /* 这块注释内容不会出现在生成的css中 */: #333;
  padding: 1; /* 这块注释内容也不会出现在生成的css中 */ 0;
}

混合器

如果你的项目中有小部分样式类似,那么使用变量来处理就可以了,当有大段样式重用的时候使用sass混合器来实现。

混合器使用@mixin标识符定义,给一大段样式赋予一个名字,通过引用这个名字重用这段样式。

下面代码定义一个名为rounded-corners的混合器,来实现圆角边框。

@mixin rounded-corners {
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
}

然后就可以要你需要的地方通过@include来使用这个混合器

notice {
  background-color: green;
  border: 2px solid #00aa00;
  @include rounded-corners;
}

//sass最终生成:

.notice {
  background-color: green;
  border: 2px solid #00aa00;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
}

混合器的使用场景,避免滥用

何时使用混合器

利用混合器,可以很容易地在样式表的不同地方共享样式。

一组属性是否应该组合成一个混合器,一条经验法则就是能否为这个混合器想出一个恰当的好名字。如果你能找到一个很好的短名字来描述这些属性修饰的样式,比如rounded-cornersfancy-font或者no-bullets,那么往往能够构造一个合适的混合器。

混合器中的CSS规则

混合器中不仅可以包含属性,也可以包含css规则,包含选择器和选择器中的属性,如下代码:

@mixin no-bullets {
  list-style: none;
  li {
    list-style-image: none;
    list-style-type: none;
    margin-left: 0px;
  }
}

通过@include把混合器包含在一个父规则中时,在混合器中的规则最终会生成父规则中的嵌套规则

ul.plain {
  color: #444;
  @include no-bullets;
}

sass@include指令会将引入混合器的那行代码替换成混合器里边的内容。最终,上边的例子如下代码:

ul.plain {
  color: #444;
  list-style: none;
}
ul.plain li {
  list-style-image: none;
  list-style-type: none;
  margin-left: 0px;
}

给混合器传参

混合器并不一定总得生成相同的样式。可以通过在@include混合器时给混合器传参,来定制混合器生成的精确样式。当@include混合器时,参数其实就是可以赋值给css属性值的变量。类似于JavaScript中的函数:

@mixin link-colors($normal, $hover, $visited) {
  color: $normal;
  &:hover { color: $hover; }
  &:visited { color: $visited; }
}

@include时,你可以把它当作一个css函数来传参

a {
  @include link-colors(blue, red, green);
}

//Sass最终生成的是:

a { color: blue; }
a:hover { color: red; }
a:visited { color: green; }

使用@include混合器时,有时候可能很难区分每个参数是什么意思,以及参数的顺序。为了解决这个问题,sass允许通过语法$name: value的形式指定每个参数的值。这种形式的传参,就不必在乎参数的顺序了,只需要保证没有漏掉参数即可:

a {
    @include link-colors(
      $normal: blue,
      $visited: green,
      $hover: red
  );
}

默认参数值

尽管给混合器加参数来实现定制很好,但是有时有些参数我们没有定制的需要,这时候也需要赋值一个变量就变成很痛苦的事情了。所以sass允许混合器声明时给参数赋默认值。

默认参数使用$name: default-value的声明形式,默认值可以是任何有效的css属性值,甚至是其他参数的引用,如下代码:

@mixin link-colors(
    $normal,
    $hover: $normal,
    $visited: $normal
  )
{
  color: $normal;
  &:hover { color: $hover; }
  &:visited { color: $visited; }
}

如果像这样调用:@include link-colors(red) $hover$visited也会被自动赋值为red

选择器继承

选择器继承是说一个选择器可以继承为另一个选择器定义的所有样式。这个通过@extend语法实现,如下代码:

//通过选择器继承样式
.error {
  border: 1px solid red;
  background-color: #fdd;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

编译为

//通过选择器继承样式
.error,.seriousError {
  border: 1px solid red;
  background-color: #fdd;
}
.seriousError {
  border-width: 3px;
}

从而最终修饰效果为class="seriousError" ,而以前页面可能是class="seriousError error"

.seriousError不仅会继承.error自身的所有样式,任何跟.error有关的组合选择器样式也会被.seriousError以组合选择器的形式继承,如下代码:

//.seriousError从.error继承样式
.error a{  //应用到.seriousError a
  color: red;
  font-weight: 100;
}
h1.error { //应用到hl.seriousError
  font-size: 1.2rem;
}

何时使用继承

继承是基于类的(有时是基于其他类型的选择器),所以继承应该是建立在语义化的关系上。当一个元素拥有的类(比如说.seriousError)表明它属于另一个类(比如说.error),这时使用继承再合适不过了。

这有点抽象,所以我们从几个方面来阐释一下。想象一下你正在编写一个页面,给html元素添加类名,你发现你的某个类(比如说.seriousError)另一个类(比如说.error)的细化。你会怎么做?

  1. 你可以为这两个类分别写相同的样式,但是如果有大量的重复怎么办?使用sass时,我们提倡的就是不要做重复的工作。
  2. 你可以使用一个选择器组(比如说.error.seriousError)给这两个选择器写相同的样式。如果.error的所有样式都在同一个地方,这种做法很好,但是如果是分散在样式表的不同地方呢?再这样做就困难多了。
  3. 你可以使用一个混合器为这两个类提供相同的样式,但当.error的样式修饰遍布样式表中各处时,这种做法面临着跟使用选择器组一样的问题。这两个类也不是恰好有相同的 样式。你应该更清晰地表达这种关系。
  4. 综上所述你应该使用@extend。让.seriousError.error继承样式,使两者之间的关系非常清晰。更重要的是无论你在样式表的哪里使用.error.seriousError都会继承其中的样式。

现在你已经更好地掌握了何时使用继承,以及继承有哪些突出的优点,接下来我们看看一些高级用法。

继承的高级用法

任何css规则都可以继承其他规则,几乎任何css规则也都可以被继承。大多数情况你可能只想对类使用继承,但是有些场合你可能想做得更多。最常用的一种高级用法是继承一个html元素的样式。尽管默认的浏览器样式不会被继承,因为它们不属于样式表中的样式,但是你对html元素添加的所有样式都会被继承。

接下来的这段代码定义了一个名为disabled的类,样式修饰使它看上去像一个灰掉的超链接。通过继承a这一超链接元素来实现:

.disabled {
  color: gray;
  @extend a;
}

假如一条样式规则继承了一个复杂的选择器,那么它只会继承这个复杂选择器命中的元素所应用的样式。举例来说, 如果.seriousError@extend.important.error , 那么.important.errorh1.important.error 的样式都会被.seriousError继承, 但是.important或者.error下的样式则不会被继承。这种情况下你很可能希望.seriousError能够分别继承.important或者.error下的样式。

如果一个选择器序列(#main .seriousError@extend另一个选择器(.error),那么只有完全匹配#main .seriousError这个选择器的元素才会继承.error的样式,就像单个类 名继承那样。拥有class="seriousError"#main元素之外的元素不会受到影响。

#main .error这种选择器序列是不能被继承的。这是因为从#main .error中继承的样式一般情况下会跟直接从.error中继承的样式基本一致,细微的区别往往使人迷惑。

现在你已经了解了通过继承能够做些什么事情,接下来我们将学习继承的工作细节,在生成对应css的时候,sass具体干了些什么事情。

继承的工作细节

跟变量和混合器不同,继承不是仅仅用css样式替换@extend处的代码那么简单。为了不让你对生成的css感觉奇怪,对这背后的工作原理有一定了解是非常重要的。

@extend背后最基本的想法是,如果.seriousError @extend .error, 那么样式表中的任何一处.error都用.error.seriousError这一选择器组进行替换。这就意味着相关样式会如预期那样应用到.error.seriousError。当.error出现在复杂的选择器中,比如说h1.error.error a或者#main .sidebar input.error[type="text"],那情况就变得复杂多了,但是不用担心,sass已经为你考虑到了这些。

关于@extend有两个要点你应该知道。

跟混合器相比,继承生成的css代码相对更少。因为继承仅仅是重复选择器,而不会重复属性,所以使用继承往往比混合器生成的css体积更小。如果你非常关心你站点的速度,请牢记这一点。

继承遵从css层叠的规则。当两个不同的css规则应用到同一个html元素上时,并且这两个不同的css规则对同一属性的修饰存在不同的值,css层叠规则会决定应用哪个样式。相当直观:通常权重更高的选择器胜出,如果权重相同,定义在后边的规则胜出。

混合器本身不会引起css层叠的问题,因为混合器把样式直接放到了css规则中,而继承存在样式层叠的问题。被继承的样式会保持原有定义位置和选择器权重不变。通常来说这并不会引起什么问题,但是知道这点总没有坏处。

使用继承的最佳实践

通常使用继承会让你的css美观、整洁。因为继承只会在生成css时复制选择器,而不会复制大段的css属性。但是如果你不小心,可能会让生成的css中包含大量的选择器复制。

避免这种情况出现的最好方法就是不要在css规则中使用后代选择器(比如.foo .bar)去继承css规则。如果你这么做,同时被继承的css规则有通过后代选择器修饰的样式,生成css中的选择器的数量很快就会失控:

.foo .bar { @extend .baz; }
.bip .baz { a: b; }

在上边的例子中,sass必须保证应用到.baz的样式同时也要应用到.foo .bar(位于class="foo"的元素内的class="bar"的元素)。例子中有一条应用到.bip .baz(位于class="bip"的元素内的class="baz"的元素)的css规则。当这条规则应用到.foo .bar时,可能存在三种情况,如下代码:

<!-- 继承可能迅速变复杂 -->
<!-- Case 1 -->
<div class="foo">
  <div class="bip">
    <div class="bar">...</div>
  </div>
</div>
<!-- Case 2 -->
<div class="bip">
  <div class="foo">
    <div class="bar">...</div>
  </div>
</div>
<!-- Case 3 -->
<div class="foo bip">
  <div class="bar">...</div>
</div>

为了应付这些情况,sass必须生成三种选择器组合(仅仅是.bip .foo .bar不能覆盖所有情况)。如果任何一条规则里边的后代选择器再长一点,sass需要考虑的情况就会更多。实际上sass并不总是会生成所有可能的选择器组合,即使是这样,选择器的个数依然可能会变得相当大,所以如果允许,尽可能避免这种用法。

值得一提的是,只要你想,你完全可以放心地继承有后代选择器修饰规则的选择器,不管后代选择器多长,但有一个前提就是,不要用后代选择器去继承。

分类栏目
© 2018邮箱:11407215#qq.comGitHub沪ICP备12039518号-6