### 1\. 使用變量;
`sass`讓人們受益的一個重要特性就是它為`css`引入了變量。你可以把反復使用的`css`屬性值 定義成變量,然后通過變量名來引用它們,而無需重復書寫這一屬性值。或者,對于僅使用過一 次的屬性值,你可以賦予其一個易懂的變量名,讓人一眼就知道這個屬性值的用途。
`sass`使用`$`符號來標識變量(老版本的`sass`使用`!`來標識變量。改成$是多半因為`!highlight-color`看起來太丑了。),比如`$highlight-color`和`$sidebar-width`。為什么選擇`$`?符號呢?因為它好認、更具美感,且在CSS中并無他用,不會導致與現存或未來的`css`語法沖突。
### 1-1\. 變量聲明;
`sass`變量的聲明和`css`屬性的聲明很像:
~~~
$highlight-color: #F90;
~~~
這意味著變量`$highlight-color`現在的值是`#F90`。任何可以用作`css`屬性值的賦值都 可以用作`sass`的變量值,甚至是以空格分割的多個屬性值,如`$basic-border: 1px solid black;`,或以逗號分割的多個屬性值,如`$plain-font: "Myriad Pro"、Myriad、"Helvetica Neue"、Helvetica、"Liberation Sans"、Arial和sans-serif; sans-serif;`。這時變 量還沒有生效,除非你引用這個變量——我們很快就會了解如何引用。
與`CSS`屬性不同,變量可以在`css`規則塊定義之外存在。當變量定義在`css`規則塊內,那么該變量只能在此規則塊內使用。如果它們出現在任何形式的`{...}`塊中(如`@media`或者`@font-face`塊),情況也是如此:
~~~
$nav-color: #F90;
nav {
$width: 100px;
width: $width;
color: $nav-color;
}
//編譯后
nav {
width: 100px;
color: #F90;
}
~~~
在這段代碼中,`$nav-color`這個變量定義在了規則塊外邊,所以在這個樣式表中都可以像?`nav`規則塊那樣引用它。`$width`這個變量定義在了`nav`的`{ }`規則塊內,所以它只能在`nav`規則塊 內使用。這意味著是你可以在樣式表的其他地方定義和使用`$width`變量,不會對這里造成影響。
只聲明變量其實沒啥用處,我們最終的目的還是使用它們。上例已介紹了如何使用?`$nav-color`和`$width`這兩個變量,接下來我們將進一步探討變量的使用方法。
### 1-2\. 變量引用;
凡是`css`屬性的標準值(比如說1px或者bold)可存在的地方,變量就可以使用。`css`生成時,變量會被它們的值所替代。之后,如果你需要一個不同的值,只需要改變這個變量的值,則所有引用此變量的地方生成的值都會隨之改變。
~~~
$highlight-color: #F90;
.selected {
border: 1px solid $highlight-color;
}
//編譯后
.selected {
border: 1px solid #F90;
}
~~~
看上邊示例中的`$highlight-color`變量,它被直接賦值給`border`屬性,當這段代碼被編譯輸出`css`時,`$highlight-color`會被`#F90`這一顏色值所替代。產生的效果就是給`selected`這個類一條1像素寬、實心且顏色值為`#F90`的邊框。
在聲明變量時,變量值也可以引用其他變量。當你通過粒度區分,為不同的值取不同名字時,這相當有用。下例在獨立的顏色值粒度上定義了一個變量,且在另一個更復雜的邊框值粒度上也定義了一個變量:
~~~
$highlight-color: #F90;
$highlight-border: 1px solid $highlight-color;
.selected {
border: $highlight-border;
}
//編譯后
.selected {
border: 1px solid #F90;
}
~~~
這里,`$highlight-border`變量的聲明中使用了`$highlight-color`這個變量。產生的效 果就跟你直接為`border`屬性設置了一個`1px`?`$highlight-color solid`的值是一樣的。 最后,我們來了解一下變量命名的實用技巧,以結束關于變量的介紹。
### 1-3\. 變量名用中劃線還是下劃線分隔;
`sass`的變量名可以與`css`中的屬性名和選擇器名稱相同,包括中劃線和下劃線。這完全取決于個人的喜好,有些人喜歡使用中劃線來分隔變量中的多個詞(如`$highlight-color`),而有些人喜歡使用下劃線(如`$highlight_color`)。使用中劃線的方式更為普遍,這也是`compass`和本文都用的方式。
不過,`sass`并不想強迫任何人一定使用中劃線或下劃線,所以這兩種用法相互兼容。用中劃線聲明的變量可以使用下劃線的方式引用,反之亦然。這意味著即使`compass`選擇用中劃線的命名方式,這并不影響你在使用`compass`的樣式中用下劃線的命名方式進行引用:
~~~
$link-color: blue;
a {
color: $link_color;
}
//編譯后
a {
color: blue;
}
~~~
在上例中,`$link-color`和`$link_color`其實指向的是同一個變量。實際上,在`sass`的大 多數地方,中劃線命名的內容和下劃線命名的內容是互通的,除了變量,也包括對混合器和Sass函數的命名。但是在`sass`中純`css`部分不互通,比如類名、ID或屬性名。
盡管變量自身提供了很多有用的地方,但是`sass`基于變量提供的更為強大的工具才是我們關注的焦點。只有當變量與`sass`的其他特性一起使用時,才能發揮其全部的潛能。接下來,我們將探討其中一個非常重要的特性,即規則嵌套。
### 2\. 嵌套CSS 規則;
`css`中重復寫選擇器是非常惱人的。如果要寫一大串指向頁面中同一塊的樣式時,往往需要 一遍又一遍地寫同一個`ID`:
~~~
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }
~~~
像這種情況,`sass`可以讓你只寫一遍,且使樣式可讀性更高。在Sass中,你可以像俄羅斯套娃那樣在規則塊中嵌套規則塊。`sass`在輸出`css`時會幫你把這些嵌套規則處理好,避免你的重復書寫。
~~~
#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 }
~~~
上邊的例子,會在輸出`css`時把它轉換成跟你之前看到的一樣的效果。這個過程中,`sass`用了兩步,每一步都是像打開俄羅斯套娃那樣把里邊的嵌套規則塊一個個打開。首先,把`#content`(父級)這個`id`放到`article`選擇器(子級)和`aside`選擇器(子級)的前邊:
~~~
#content {
article {
h1 { color: #333 }
p { margin-bottom: 1.4em }
}
#content aside { background-color: #EEE }
}
~~~
~~~
/* 編譯后 */
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }
~~~
然后,`#content article`里邊還有嵌套的規則,`sass`重復一遍上邊的步驟,把新的選擇器添加到內嵌的選擇器前邊。
一個給定的規則塊,既可以像普通的CSS那樣包含屬性,又可以嵌套其他規則塊。當你同時要為一個容器元素及其子元素編寫特定樣式時,這種能力就非常有用了。
~~~
#content {
background-color: #f5f5f5;
aside { background-color: #eee }
}
~~~
容器元素的樣式規則會被單獨抽離出來,而嵌套元素的樣式規則會像容器元素沒有包含任何屬性時那樣被抽離出來。
~~~
#content { background-color: #f5f5f5 }
#content aside { background-color: #eee }
~~~
大多數情況下這種簡單的嵌套都沒問題,但是有些場景下不行,比如你想要在嵌套的選擇器 里邊立刻應用一個類似于`:hover`的偽類。為了解決這種以及其他情況,`sass`提供了一個特殊結 構`&`。
### 2-1\. 父選擇器的標識符&;
一般情況下,`sass`在解開一個嵌套規則時就會把父選擇器(`#content`)通過一個空格連接到子選擇器的前邊(`article`和`aside`)形成(`#content article`和`#content aside`)。這種在CSS里邊被稱為后代選擇器,因為它選擇ID為`content`的元素內所有命中選擇器`article`和`aside`的元素。但在有些情況下你卻不會希望`sass`使用這種后代選擇器的方式生成這種連接。
最常見的一種情況是當你為鏈接之類的元素寫`:hover`這種偽類時,你并不希望以后代選擇器的方式連接。比如說,下面這種情況`sass`就無法正常工作:
~~~
article a {
color: blue;
:hover { color: red }
}
~~~
這意味著`color: red`這條規則將會被應用到選擇器`article a :hover`,`article`元素內鏈接的所有子元素在被`hover`時都會變成紅色。這是不正確的!你想把這條規則應用到超鏈接自身,而后代選擇器的方式無法幫你實現。
解決之道為使用一個特殊的`sass`選擇器,即父選擇器。在使用嵌套規則時,父選擇器能對于嵌套規則如何解開提供更好的控制。它就是一個簡單的`&`符號,且可以放在任何一個選擇器可出現的地方,比如`h1`放在哪,它就可以放在哪。
~~~
article a {
color: blue;
&:hover { color: red }
}
~~~
當包含父選擇器標識符的嵌套規則被打開時,它不會像后代選擇器那樣進行拼接,而是`&`被父選擇器直接替換:
~~~
article a { color: blue }
article a:hover { color: red }
~~~
在為父級選擇器添加`:hover`等偽類時,這種方式非常有用。同時父選擇器標識符還有另外一種用法,你可以在父選擇器之前添加選擇器。舉例來說,當用戶在使用IE瀏覽器時,你會通過`JavaScript`在`<body>`標簽上添加一個ie的類名,為這種情況編寫特殊的樣式如下:
~~~
#content aside {
color: red;
body.ie & { color: green }
}
~~~
~~~
/*編譯后*/
#content aside {color: red};
body.ie #content aside { color: green }
~~~
`sass`在選擇器嵌套上是非常智能的,即使是帶有父選擇器的情況。當`sass`遇到群組選擇器(由多個逗號分隔開的選擇器形成)也能完美地處理這種嵌套。
### 2-2\. 群組選擇器的嵌套;
在`CSS`里邊,選擇器`h1``h2`和`h3`會同時命中h1元素、h2元素和h3元素。與此類似,`.button`?`button`會命中button元素和類名為.button的元素。這種選擇器稱為群組選擇器。群組選擇器 的規則會對命中群組中任何一個選擇器的元素生效。
~~~
.button, button {
margin: 0;
}
~~~
當看到上邊這段代碼時,你可能還沒意識到會有重復性的工作。但會很快發現:如果你需要在一個特定的容器元素內對這樣一個群組選擇器進行修飾,情況就不同了。`css`的寫法會讓你在群組選擇器中的每一個選擇器前都重復一遍容器元素的選擇器。
~~~
.container h1, .container h2, .container h3 { margin-bottom: .8em }
~~~
非常幸運,`sass`的嵌套特性在這種場景下也非常有用。當`sass`解開一個群組選擇器規則內嵌的規則時,它會把每一個內嵌選擇器的規則都正確地解出來:
~~~
.container {
h1, h2, h3 {margin-bottom: .8em}
}
~~~
首先`sass`將`.container`和`h1``.container`和`h2``.container`和`h3`分別組合,然后將三 者重新組合成一個群組選擇器,生成你前邊看到的普通`css`樣式。對于內嵌在群組選擇器內的嵌 套規則,處理方式也一樣:
~~~
nav, aside {
a {color: blue}
}
~~~
首先`sass`將`nav`和`a``aside`和`a`分別組合,然后將二者重新組合成一個群組選擇器:
~~~
nav a, aside a {color: blue}
~~~
處理這種群組選擇器規則嵌套上的強大能力,正是`sass`在減少重復敲寫方面的貢獻之一。尤其在當嵌套級別達到兩層甚至三層以上時,與普通的`css`編寫方式相比,只寫一遍群組選擇器大大減少了工作量。
有利必有弊,你需要特別注意群組選擇器的規則嵌套生成的`css`。雖然`sass`讓你的樣式表看上去很小,但實際生成的`css`卻可能非常大,這會降低網站的速度。
關于選擇器嵌套的最后一個方面,我們看看`sass`如何處理組合選擇器,比如>、+和~的使用。你將看到,這種場景下你甚至無需使用父選擇器標識符。
### 2-3\. 子組合選擇器和同層組合選擇器:>、+和~;
上邊這三個組合選擇器必須和其他選擇器配合使用,以指定瀏覽器僅選擇某種特定上下文中的元素。
~~~
article section { margin: 5px }
article > section { border: 1px solid #ccc }
~~~
你可以用子組合選擇器>選擇一個元素的直接子元素。上例中,第一個選擇器會選擇article下的所有命中section選擇器的元素。第二個選擇器只會選擇article下緊跟著的子元素中命中section選擇器的元素。
在下例中,你可以用同層相鄰組合選擇器`+`選擇`header`元素后緊跟的`p`元素:
~~~
header + p { font-size: 1.1em }
~~~
你也可以用同層全體組合選擇器`~`,選擇所有跟在`article`后的同層`article`元素,不管它們之間隔了多少其他元素:
~~~
article ~ article { border-top: 1px dashed #ccc }
~~~
這些組合選擇器可以毫不費力地應用到`sass`的規則嵌套中。可以把它們放在外層選擇器后邊,或里層選擇器前邊:
~~~
article {
~ article { border-top: 1px dashed #ccc }
> section { background: #eee }
dl > {
dt { color: #333 }
dd { color: #555 }
}
nav + & { margin-top: 0 }
}
~~~
`sass`會如你所愿地將這些嵌套規則一一解開組合在一起:
~~~
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`規則可以嵌套,對屬性進行嵌套也可以減少很多重復性的工作。
### 2-4\. 嵌套屬性;
在`sass`中,除了CSS選擇器,屬性也可以進行嵌套。盡管編寫屬性涉及的重復不像編寫選擇器那么糟糕,但是要反復寫`border-style``border-width``border-color`以及`border-*`等也是非常煩人的。在`sass`中,你只需敲寫一遍`border`:
~~~
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`通過對`css`原有`@import`規則的改進直接支持了這一特性。
### 3\. 導入SASS文件;
`css`有一個特別不常用的特性,即`@import`規則,它允許在一個`css`文件中導入其他`css`文件。然而,后果是只有執行到`@import`時,瀏覽器才會去下載其他`css`文件,這導致頁面加載起來特別慢。
`sass`也有一個`@import`規則,但不同的是,`sass`的`@import`規則在生成`css`文件時就把相關文件導入進來。這意味著所有相關的樣式被歸納到了同一個`css`文件中,而無需發起額外的下載請求。另外,所有在被導入文件中定義的變量和混合器(參見2.5節)均可在導入文件中使用。
使用`sass`的`@import`規則并不需要指明被導入文件的全名。你可以省略`.sass`或`.scss`文件后綴(見下圖)。這樣,在不修改樣式表的前提下,你完全可以隨意修改你或別人寫的被導入的`sass`樣式文件語法,在`sass`和`scss`語法之間隨意切換。舉例來說,`@import`"sidebar";這條命令將把`sidebar.scss`文件中所有樣式添加到當前樣式表中。

本節將介紹如何使用`sass`的`@import`來處理多個`sass`文件。首先,我們將學習編寫那些被導入的`sass`文件,因為在一個大型`sass`項目中,這樣的文件是你最常編寫的那一類。接著,了解集中導入`sass`文件的方法,使你的樣式可重用性更高,包括聲明可自定義的變量值,以及在某一個選擇器范圍內導入`sass`文件。最后,介紹如何在`sass`中使用`css`原生的`@import`命令。
通常,有些`sass`文件用于導入,你并不希望為每個這樣的文件單獨地生成一個`css`文件。對此,`sass`用一個特殊的約定來解決。
### 3-1\. 使用SASS部分文件;
當通過`@import`把`sass`樣式分散到多個文件時,你通常只想生成少數幾個`css`文件。那些專門為`@import`命令而編寫的`sass`文件,并不需要生成對應的獨立`css`文件,這樣的`sass`文件稱為局部文件。對此,`sass`有一個特殊的約定來命名這些文件。
此約定即,`sass`局部文件的文件名以下劃線開頭。這樣,`sass`就不會在編譯時單獨編譯這個文件輸出`css`,而只把這個文件用作導入。當你`@import`一個局部文件時,還可以不寫文件的全名,即省略文件名開頭的下劃線。舉例來說,你想導入`themes/_night-sky.scss`這個局部文件里的變量,你只需在樣式表中寫`@import``"themes/night-sky";`。
局部文件可以被多個不同的文件引用。當一些樣式需要在多個頁面甚至多個項目中使用時,這非常有用。在這種情況下,有時需要在你的樣式表中對導入的樣式稍作修改,`sass`有一個功能剛好可以解決這個問題,即默認變量值。
### 3-2\. 默認變量值;
一般情況下,你反復聲明一個變量,只有最后一處聲明有效且它會覆蓋前邊的值。舉例說明:
~~~
$link-color: blue;
$link-color: red;
a {
color: $link-color;
}
~~~
在上邊的例子中,超鏈接的`color`會被設置為`red`。這可能并不是你想要的結果,假如你寫了一個可被他人通過`@import`導入的`sass`庫文件,你可能希望導入者可以定制修改`sass`庫文件中的某些值。使用`sass`的`!default`標簽可以實現這個目的。它很像`css`屬性中`!important`標簽的對立面,不同的是`!default`用于變量,含義是:如果這個變量被聲明賦值了,那就用它聲明的值,否則就用這個默認值。
~~~
$fancybox-width: 400px !default;
.fancybox {
width: $fancybox-width;
}
~~~
在上例中,如果用戶在導入你的`sass`局部文件之前聲明了一個`$fancybox-width`變量,那么你的局部文件中對`$fancybox-width`賦值`400px`的操作就無效。如果用戶沒有做這樣的聲明,則`$fancybox-width`將默認為`400px`。
接下來我們將學習嵌套導入,它允許只在某一個選擇器的范圍內導入`sass`局部文件。
### 3-3\. 嵌套導入;
跟原生的`css`不同,`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;
}
}
~~~
被導入的局部文件中定義的所有變量和混合器,也會在這個規則范圍內生效。這些變量和混合器不會全局有效,這樣我們就可以通過嵌套導入只對站點中某一特定區域運用某種顏色主題或其他通過變量配置的樣式。
有時,可用`css`原生的`@import`機制,在瀏覽器中下載必需的`css`文件。`sass`也提供了幾種方法來達成這種需求。
### 3-4\. 原生的CSS導入;
由于`sass`兼容原生的`css`,所以它也支持原生的`CSS@import`。盡管通常在`sass`中使用`@import`時,`sass`會嘗試找到對應的`sass`文件并導入進來,但在下列三種情況下會生成原生的`CSS@import`,盡管這會造成瀏覽器解析`css`時的額外下載:
* 被導入文件的名字以`.css`結尾;
* 被導入文件的名字是一個URL地址(比如http://www.sass.hk/css/css.css),由此可用谷歌字體API提供的相應服務;
* 被導入文件的名字是`CSS`的url()值。
這就是說,你不能用`sass`的`@import`直接導入一個原始的`css`文件,因為`sass`會認為你想用`css`原生的`@import`。但是,因為`sass`的語法完全兼容`css`,所以你可以把原始的`css`文件改名為`.scss`后綴,即可直接導入了。
文件導入是保證`sass`的代碼可維護性和可讀性的重要一環。次之但亦非常重要的就是注釋了。注釋可以幫助樣式作者記錄寫`sass`的過程中的想法。在原生的`css`中,注釋對于其他人是直接可見的,但`sass`提供了一種方式可在生成的`css`文件中按需抹掉相應的注釋。
### 4\. 靜默注釋;
`css`中注釋的作用包括幫助你組織樣式、以后你看自己的代碼時明白為什么這樣寫,以及簡單的樣式說明。但是,你并不希望每個瀏覽網站源碼的人都能看到所有注釋。
`sass`另外提供了一種不同于`css`標準注釋格式`/* ... */`的注釋語法,即靜默注釋,其內容不會出現在生成的`css`文件中。靜默注釋的語法跟`JavaScript``Java`等類`C`的語言中單行注釋的語法相同,它們以`//`開頭,注釋內容直到行末。
~~~
body {
color: #333; // 這種注釋內容不會出現在生成的css文件中
padding: 0; /* 這種注釋內容會出現在生成的css文件中 */
}
~~~
實際上,`css`的標準注釋格式`/* ... */`內的注釋內容亦可在生成的`css`文件中抹去。當注釋出現在原生`css`不允許的地方,如在`css`屬性或選擇器中,`sass`將不知如何將其生成到對應`css`文件中的相應位置,于是這些注釋被抹掉。
~~~
body {
color /* 這塊注釋內容不會出現在生成的css中 */: #333;
padding: 1; /* 這塊注釋內容也不會出現在生成的css中 */ 0;
}
~~~
你已經掌握了`sass`的靜默注釋,了解了保持`sass`條理性和可讀性的最基本的三個方法:嵌套、導入和注釋。現在,我們要進一步學習新特性,這樣我們不但能保持條理性還能寫出更好的樣式。首先要介紹的內容是:使用混合器抽象你的相關樣式。
### 5\. 混合器;
如果你的整個網站中有幾處小小的樣式類似(例如一致的顏色和字體),那么使用變量來統一處理這種情況是非常不錯的選擇。但是當你的樣式變得越來越復雜,你需要大段大段的重用樣式的代碼,獨立的變量就沒辦法應付這種情況了。你可以通過`sass`的混合器實現大段樣式的重用。
混合器使用`@mixin`標識符定義。看上去很像其他的`CSS @`標識符,比如說`@media`或者`@font-face`。這個標識符給一大段樣式賦予一個名字,這樣你就可以輕易地通過引用這個名字重用這段樣式。下邊的這段`sass`代碼,定義了一個非常簡單的混合器,目的是添加跨瀏覽器的圓角邊框。
~~~
@mixin rounded-corners {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
~~~
然后就可以在你的樣式表中通過`@include`來使用這個混合器,放在你希望的任何地方。`@include`調用會把混合器中的所有樣式提取出來放在`@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;
}
~~~
在`.notice`中的屬性`border-radius``-moz-border-radius`和`-webkit-border-radius`全部來自`rounded-corners`這個混合器。這一節將介紹使用混合器來避免重復。通過使用參數,你可以使用混合器把你樣式中的通用樣式抽離出來,然后輕松地在其他地方重用。實際上,混合器太好用了,一不小心你可能會過度使用。大量的重用可能會導致生成的樣式表過大,導致加載緩慢。所以,首先我們將討論混合器的使用場景,避免濫用。
### 5-1\. 何時使用混合器;
利用混合器,可以很容易地在樣式表的不同地方共享樣式。如果你發現自己在不停地重復一段樣式,那就應該把這段樣式構造成優良的混合器,尤其是這段樣式本身就是一個邏輯單元,比如說是一組放在一起有意義的屬性。
判斷一組屬性是否應該組合成一個混合器,一條經驗法則就是你能否為這個混合器想出一個好的名字。如果你能找到一個很好的短名字來描述這些屬性修飾的樣式,比如`rounded-corners``fancy-font`或者`no-bullets`,那么往往能夠構造一個合適的混合器。如果你找不到,這時候構造一個混合器可能并不合適。
混合器在某些方面跟`css`類很像。都是讓你給一大段樣式命名,所以在選擇使用哪個的時候可能會產生疑惑。最重要的區別就是類名是在`html`文件中應用的,而混合器是在樣式表中應用的。這就意味著類名具有語義化含義,而不僅僅是一種展示性的描述:用來描述`html`元素的含義而不是`html`元素的外觀。而另一方面,混合器是展示性的描述,用來描述一條`css`規則應用之后會產生怎樣的效果。
在之前的例子中,`.notice`是一個有語義的類名。如果一個`html`元素有一個`notice`的類名,就表明了這個`html`元素的用途:向用戶展示提醒信息。`rounded-corners`混合器是展示性的,它描述了包含它的`css`規則最終的視覺樣式,尤其是邊框角的視覺樣式。混合器和類配合使用寫出整潔的`html`和`css`,因為使用語義化的類名亦可以幫你避免重復使用混合器。為了保持你的`html`和`css`的易讀性和可維護性,在寫樣式的過程中一定要銘記二者的區別。
有時候僅僅把屬性放在混合器中還遠遠不夠,可喜的是,`sass`同樣允許你把`css`規則放在混合器中。
### 5-2\. 混合器中的CSS規則;
混合器中不僅可以包含屬性,也可以包含`css`規則,包含選擇器和選擇器中的屬性,如下代碼:
~~~
@mixin no-bullets {
list-style: none;
li {
list-style-image: none;
list-style-type: none;
margin-left: 0px;
}
}
~~~
當一個包含`css`規則的混合器通過`@include`包含在一個父規則中時,在混合器中的規則最終會生成父規則中的嵌套規則。舉個例子,看看下邊的`sass`代碼,這個例子中使用了`no-bullets`這個混合器:
~~~
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;
}
~~~
混合器中的規則甚至可以使用`sass`的父選擇器標識符`&`。使用起來跟不用混合器時一樣,`sass`解開嵌套規則時,用父規則中的選擇器替代`&`。
如果一個混合器只包含`css`規則,不包含屬性,那么這個混合器就可以在文檔的頂部調用,寫在所有的`css`規則之外。如果你只是為自己寫一些混合器,這并沒有什么大的用途,但是當你使用一個類似于`Compass`的庫時,你會發現,這是提供樣式的好方法,原因在于你可以選擇是否使用這些樣式。
接下來你將學習如何通過給混合器傳參數來讓混合器變得更加靈活和可重用。
### 5-3\. 給混合器傳參;
混合器并不一定總得生成相同的樣式。可以通過在`@include`混合器時給混合器傳參,來定制混合器生成的精確樣式。當`@include`混合器時,參數其實就是可以賦值給`css`屬性值的變量。如果你寫過`JavaScript`,這種方式跟`JavaScript`的`function`很像:
~~~
@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`允許混合器聲明時給參數賦默認值。
### 5-4\. 默認參數值;
為了在`@include`混合器時不必傳入所有的參數,我們可以給參數指定一個默認值。參數默認值使用`$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`。
混合器只是`sass`樣式重用特性中的一個。我們已經了解到混合器主要用于樣式展示層的重用,如果你想重用語義化的類呢?這就涉及`sass`的另一個重要的重用特性:選擇器繼承。
### 6\. 使用選擇器繼承來精簡CSS;
使用`sass`的時候,最后一個減少重復的主要特性就是選擇器繼承。基于`Nicole Sullivan`面向對象的`css`的理念,選擇器繼承是說一個選擇器可以繼承為另一個選擇器定義的所有樣式。這個通過`@extend`語法實現,如下代碼:
~~~
//通過選擇器繼承繼承樣式
.error {
border: 1px red;
background-color: #fdd;
}
.seriousError {
@extend .error;
border-width: 3px;
}
~~~
在上邊的代碼中,`.seriousError`將會繼承樣式表中任何位置處為`.error`定義的所有樣式。以`class="seriousError"`?修飾的`html`元素最終的展示效果就好像是`class="seriousError error"`。相關元素不僅會擁有一個`3px`寬的邊框,而且這個邊框將變成紅色的,這個元素同時還會有一個淺紅色的背景,因為這些都是在`.error`里邊定義的樣式。
`.seriousError`不僅會繼承`.error`自身的所有樣式,任何跟`.error`有關的組合選擇器樣式也會被`.seriousError`以組合選擇器的形式繼承,如下代碼:
~~~
//.seriousError從.error繼承樣式
.error a{ //應用到.seriousError a
color: red;
font-weight: 100;
}
h1.error { //應用到hl.seriousError
font-size: 1.2rem;
}
~~~
如上所示,在`class="seriousError"`的`html`元素內的超鏈接也會變成紅色和粗體。
本節將介紹與混合器相比,哪種情況下更適合用繼承。接下來在探索繼承的工作細節之前,我們先了解一下繼承的高級用法。最后,我們將看看使用繼承可能會有哪些坑,學習如何避免這些坑。
### 6-1\. 何時使用繼承;
5-1節介紹了[混合器](http://www.sass.hk/sass-course.html)主要用于展示性樣式的重用,而類名用于語義化樣式的重用。因為繼承是基于類的(有時是基于其他類型的選擇器),所以繼承應該是建立在語義化的關系上。當一個元素擁有的類(比如說`.seriousError`)表明它屬于另一個類(比如說`.error`),這時使用繼承再合適不過了。
這有點抽象,所以我們從幾個方面來闡釋一下。想象一下你正在編寫一個頁面,給`html`元素添加類名,你發現你的某個類(比如說`.seriousError`)另一個類(比如說`.error`)的細化。你會怎么做?
* 你可以為這兩個類分別寫相同的樣式,但是如果有大量的重復怎么辦?使用`sass`時,我們提倡的就是不要做重復的工作。
* 你可以使用一個選擇器組(比如說`.error``.seriousError`)給這兩個選擇器寫相同的樣式。如果.error的所有樣式都在同一個地方,這種做法很好,但是如果是分散在樣式表的不同地方呢?再這樣做就困難多了。
* 你可以使用一個混合器為這兩個類提供相同的樣式,但當`.error`的樣式修飾遍布樣式表中各處時,這種做法面臨著跟使用選擇器組一樣的問題。這兩個類也不是恰好有相同的 樣式。你應該更清晰地表達這種關系。
* 綜上所述你應該使用`@extend`。讓`.seriousError`從`.error`繼承樣式,使兩者之間的關系非常清晰。更重要的是無論你在樣式表的哪里使用`.error``.seriousError`都會繼承其中的樣式。
現在你已經更好地掌握了何時使用繼承,以及繼承有哪些突出的優點,接下來我們看看一些高級用法。
### 6-2\. 繼承的高級用法;
任何`css`規則都可以繼承其他規則,幾乎任何`css`規則也都可以被繼承。大多數情況你可能只想對類使用繼承,但是有些場合你可能想做得更多。最常用的一種高級用法是繼承一個`html`元素的樣式。盡管默認的瀏覽器樣式不會被繼承,因為它們不屬于樣式表中的樣式,但是你對`html`元素添加的所有樣式都會被繼承。
接下來的這段代碼定義了一個名為`disabled`的類,樣式修飾使它看上去像一個灰掉的超鏈接。通過繼承a這一超鏈接元素來實現:
~~~
.disabled {
color: gray;
@extend a;
}
~~~
假如一條樣式規則繼承了一個復雜的選擇器,那么它只會繼承這個復雜選擇器命中的元素所應用的樣式。舉例來說, 如果`.seriousError``@extend``.important.error`?, 那么`.important.error`?和`h1.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`具體干了些什么事情。
### 6-3\. 繼承的工作細節;
跟變量和混合器不同,繼承不是僅僅用`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`規則中,而繼承存在樣式層疊的問題。被繼承的樣式會保持原有定義位置和選擇器權重不變。通常來說這并不會引起什么問題,但是知道這點總沒有壞處。
### 6-4\. 使用繼承的最佳實踐;
通常使用繼承會讓你的`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`并不總是會生成所有可能的選擇器組合,即使是這樣,選擇器的個數依然可能會變得相當大,所以如果允許,盡可能避免這種用法。
值得一提的是,只要你想,你完全可以放心地繼承有后代選擇器修飾規則的選擇器,不管后代選擇器多長,但有一個前提就是,不要用后代選擇器去繼承。
### 7\. 小結;
本文介紹了`sass`最基本部分,你可以輕松地使用`sass`編寫清晰、無冗余、語義化的`css`。對于`sass`提供的工具你已經有了一個比較深入的了解,同時也掌握了何時使用這些工具的指導原則。
變量是`sass`提供的最基本的工具。通過變量可以讓獨立的`css`值變得可重用,無論是在一條單獨的規則范圍內還是在整個樣式表中。變量、混合器的命名甚至`sass`的文件名,可以互換通用`_`和`-`。同樣基礎的是`sass`的嵌套機制。嵌套允許`css`規則內嵌套`css`規則,減少重復編寫常用的選擇器,同時讓樣式表的結構一眼望去更加清晰。`sass`同時提供了特殊的父選擇器標識符`&`,通過它可以構造出更高效的嵌套。
你也已經學到了`sass`的另一個重要特性,樣式導入。通過樣式導入可以把分散在多個`sass`文件中的內容合并生成到一個`css`文件,避免了項目中有大量的`css`文件通過原生的`css`?`@import`帶來的性能問題。通過嵌套導入和默認變量值,導入可以構建更強有力的、可定制的樣式。混合器允許用戶編寫語義化樣式的同時避免視覺層面上樣式的重復。你不僅學到了如何使用混合器減少重復,同時學習到了如何使用混合器讓你的`css`變得更加可維護和語義化。最后,我們學習了與混合器相輔相成的選擇器繼承。繼承允許你聲明類之間語義化的關系,通過這些關系可以保持你的`css`的整潔和可維護性。