Skip to content

Sass 三種重用方式怎麼選:mixin、extend 與 function

Sass 三種重複使用方式的比較

@mixin@extend@function 常被放在一起講「都是為了重複使用」,但它們解決的其實不是同一件事。

產出什麼一句話
@mixin一段樣式,複製到呼叫處「幫我把這段寫在這裡」
@extend合併後的選取器「我跟它長得一樣」
@function一個值「幫我算出這個數字」

同一個需求的三種寫法

需求:三個按鈕變體共用基底樣式。

混入:

scss
@mixin btn-base {
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

.btn-primary { @include btn-base; background: #2965f1; }
.btn-danger  { @include btn-base; background: #e03131; }
css
.btn-primary { padding: 8px 16px; border-radius: 4px; cursor: pointer; background: #2965f1; }
.btn-danger { padding: 8px 16px; border-radius: 4px; cursor: pointer; background: #e03131; }

繼承:

scss
%btn-base {
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

.btn-primary { @extend %btn-base; background: #2965f1; }
.btn-danger  { @extend %btn-base; background: #e03131; }
css
.btn-primary, .btn-danger { padding: 8px 16px; border-radius: 4px; cursor: pointer; }
.btn-primary { background: #2965f1; }
.btn-danger { background: #e03131; }

函式做不到這件事,它回傳值,不產生宣告。函式適合的是另一種需求:

scss
@function space($n) { @return $n * 8px; }

.btn-primary { padding: space(1) space(2); }

產出的差異

混入繼承
宣告出現次數每個呼叫處各一份只有一份
選取器各自獨立合併成串列
規則位置在呼叫的地方在被繼承者定義的地方
壓縮前體積較大較小
gzip 後體積差距很小較小

能力差異

能力@mixin@extend@function
帶參數
可用在 @media
跨檔案穩定⚠️ 難追
回傳值
接收樣式區塊✅(@content
產出順序可預測不適用

@extend 那兩個 ❌ 是硬限制,也是它在現代 RWD 專案裡用不太上的原因。

決策表

你的需求用什麼
算一個數字或色彩出來@function
從 map 取值並在取不到時報錯@function
一段固定的樣式要重複寫@mixin
同一段樣式但值不同@mixin + 參數
包一層媒體查詢或主題外殼@mixin + @content
幾十個選取器共用一大段樣式,且都在同一層級@extend %placeholder
不確定@mixin

「不確定就用混入」不是敷衍

混入沒有順序問題、能帶參數、能進媒體查詢、跨檔案行為一致。它換來的成本只是壓縮後很小的體積差;@extend 換來的成本是三個月後你追不到那條選取器為什麼長那樣。

函式與混入常常一起用

典型的分工是「函式算值、混入組樣式」:

scss
@use "sass:math";

@function rem($px, $base: 16px) {
  @return math.div($px, $base) * 1rem;
}

@mixin text($size, $line: 1.5) {
  font-size: rem($size);
  line-height: $line;
}

.title { @include text(24px); }

那工具類別呢

第四種選擇:不重用 Sass,改成產生一批 工具類別 讓 HTML 直接套。

scss
@each $n in (1, 2, 3, 4) {
  .p-#{$n} { padding: $n * 8px; }
}
html
<div class="p-2">…</div>

差別在 樣式的決定權寫在哪裡,混入寫在 .scss,工具類別寫在 HTML。兩者可以並存,但一個專案該有明確的主要風格。

常見問題

不確定該用哪個的時候怎麼辦?

用混入。它沒有繼承的順序問題,可以帶參數,也能寫在媒體查詢裡,行為完全可預測。產出的重複在壓縮與 gzip 之後成本很低,遠比日後追不到樣式從哪來划算。

混入的重複輸出真的沒關係嗎?

多數情況沒關係。重複的宣告字串正是 gzip 最擅長壓縮的東西,實際傳輸量增加有限。只有在幾十個選取器共用一大段完全相同的樣式、且都在同一個媒體查詢層級時,改用繼承才划算。

函式和混入可以搭配使用嗎?

可以,而且很常見。典型的分工是函式負責算出值、混入負責把值組成宣告,例如混入內部呼叫換算函式來決定字級與行高。

工具類別和混入該選哪個?

看樣式寫在哪裡。希望把樣式決定留在 HTML 的類別上就產生工具類別,希望 HTML 只有語意類別就用混入。兩者可以並存,但同一個專案應該有明確的主要風格。

延伸閱讀