Sass - 前端工程師應該要會的工程化 CSS - @mixin 與 @extend
要使用 Sass 絕對不能錯過它提供的一些好用函式,這些函式可以幫助我們省去許多的”重工”。其中 mixin 與 extend 算是主要的大功能。
Sass 的主要功能前方都會使用 @
符號表示,雖然 Sass 本身有縮寫的版本但只提供 Sass 使用 SCSS 是不吃的,這邊就統一用 SCSS 的方式來表示囉!喜歡 Sass 寫法的可以參考官網有說明各功能的縮寫方式。
@mixin / @include
當我們在撰寫樣式碼時,很容易出現”重複”的樣式語法,它們可能都有固定模式的寫法,只是要塞入的數值不同。這時候使用 mixin 來包裝,就可以快速產生相應的語法又能依據各區塊調整成不同的數值。
1 | @mixin NAME($val1,$val2...){ |
@mixin 後方接的是這組定義的命名( NAME ),再之後的 ()
其實取決於要不要帶$參數,它是可以省略不寫的。{}
內塞的就是實際要產生的樣式語法,值寫成帶入的參數編譯後就會將參數值替換過去。要調用 @mixin 則於選擇器中 @include
。
下方以最簡單的寬度高度為例,假設很多區塊都會設定寬高,那這兩個語法就可以組成一組 mixin。
1 | // 參數也可以設定 default 值,下方表示如果 $h 沒設定就會帶入 $w 的值 |
要注意的是如果 @mixin 有設定參數,實際使用就一定要塞值給它否則編譯會出錯,或是也可像上例一樣設定預設值。
從轉出的 CSS 可以看出,@mixin
本身是不會被輸出的,他只在 Sass 的環境下運行,當 Sass 要編譯為 CSS 時取用它的規則。因此我們可以定義很多常用到的 @mixin 而不用怕 CSS 檔案變大。
@content
@mixin 提供一種”插槽”的撰寫方式,除了事先定義要寫入的語法外,也可以設置插槽於 @include 這組 mixin 時再另外設定內容。講得很玄直接看程式碼最快。假設我們想要將 media query 做成 @mixin 來用:
1 | @mixin breakpoint($point) { |
@mixin 的參數除了一個一個帶入之外,也可以帶入 list 或 map
list 範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14// $參數後方加上...,會自動將之後帶入的值視為一組 list
@mixin box-shadow($borderColor, $shadows...) {
border-color: $borderColor;
box-shadow: $shadows;
}
.shadows {
@include box-shadow(#ccc, 0px 4px 5px #666, 2px 6px 10px #999);
}
// 轉成 CSS
.shadows {
border-color: #ccc;
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
}map 範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@mixin colors($text, $background, $border) {
color: $text;
background-color: $background;
border-color: $border;
}
// 事先定義一組變數來存放 map,map 的 key 需和 @mixin 的參數名相同
$value-map: (text: #00ff00, background: #0000ff, border: #ff0000);
.box {
// 帶入 map 變數需要用...將各個值展開到各自對應的位置上
@include colors($value-map...);
}
// 轉成 CSS
.box {
color: #00ff00;
background-color: #0000ff;
border-color: #ff0000;
}
@extend / %
@extend 跟 @mixin 有點像,主要的目的也是集合共通的樣式語法,最大的不同點在於沒有參數設定、不同的輸出方式以及具有”繼承”的概念。@extend 的設定方式有兩種,一種是直接”繼承選擇器”,另一種則跟 @mixin 類似,先另外定義再於選擇器中載入。
繼承選擇器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22.error {
border: 1px #f00;
background-color: #fdd;
}
.error.intrusion {
background-image: url("/image/hacked.png");
}
.seriousError {
@extend .error; // 此處寫入繼承 .error ,這樣包含 .error 所有相關的屬性都會繼承到
border-width: 3px;
}
//轉成css
.error, .seriousError {
border: 1px #f00;
background-color: #fdd; }
.error.intrusion, .seriousError.intrusion { //附帶的class也會一併被繼承套用
background-image: url("/image/hacked.png"); }
.seriousError {
border-width: 3px; }上例可以發現,雖然
.seriousError
繼承.error
的樣式,但實際會連.error.intrusion
這組選擇器也一併繼承。輸出成 CSS 後就會自動多出一行是.seriousError.intrusion
。這在執行上可能會產生不可預料的問題( 跟個人的撰寫習慣有關 ),也會多出不必要的 CSS 設定。使用
%NAME
自定義
定義本身並不會實際轉出 CSS,於選擇器中 @extend 即可。這種方式輸出的 CSS 跟 @mixin 會不同。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// 自定義 extend
%posAbsolute{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.container{
// 於選擇器中 @extend
@extend %posAbsolute;
border: 2px solid #ccc;
}
.box{
@extend %posAbsolute;
background-color: #5444ee;
}
// 轉成 CSS
.container, .box {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.container {
border: 2px solid #ccc;
}
.box {
background-color: #5444ee;
}上例可以看出,使用 @extend 的選擇器輸出後會被統一歸納,以共通樣式的角度來輸出 CSS。@mixin 的話則是以選擇器的角度來輸出。
其實兩種方式都可以使用,只是 @extend 無法載入參數,因此較適合用來設定”固定不變”的 CSS 寫法。(像範例的絕對置中)
以上內容如有勘誤,還請不吝告知🙇