Sass 變數旗標:!default 讓變數可被覆寫、!global 的代價
變數 宣告的結尾可以再掛一個 旗標,用來改變指派的行為。Sass 有兩個:!default 讓值變成「預設值」,!global 讓區域的指派寫回全域。前者是寫可重用模組的必備工具,後者則是應該盡量繞開的寫法。
!default:沒設定過才生效
$brand: #2965f1 !default;這一行的意思是:如果 $brand 還沒有值(或值是 null),就設成 #2965f1;已經有值就什麼都不做。
$brand: #e03131;
$brand: #2965f1 !default;
.btn {
color: $brand; // #e03131,前面已經設定過了
}順序很重要,帶 !default 的那一行必須在後面,它才有機會判斷「前面是不是已經給過了」。
它真正的用途:可設定的模組
單一檔案裡用 !default 意義不大,它的價值在跨檔案。假設有一個按鈕模組:
// _button.scss
$radius: 4px !default;
$height: 40px !default;
.btn {
border-radius: $radius;
height: $height;
}使用它的人可以在載入時改掉這些值:
// main.scss
@use "button" with (
$radius: 999px
);產出的 .btn 圓角變成 999px,$height 沒指定就沿用模組自己的 40px。
沒有 !default 就不能被設定
@use ... with 只認得帶 !default 的變數,對一般變數使用會直接編譯失敗。所以模組作者要想清楚:哪些變數是對外開放的設定項,就加 !default。 詳細規則見 @use。
null 也算「還沒設定」
$shadow: null;
$shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !default;
.card {
box-shadow: $shadow; // 陰影會生效
}null 被視為沒有值,所以 !default 依然會套用。這讓「先宣告佔位、之後再決定」的寫法變得可行。
!global:把區域指派寫回全域
預設情況下,區塊裡的指派只影響區塊內:
$count: 0;
@mixin add {
$count: $count + 1; // 只改到區域的副本
}加上 !global 才會真的改到外層那一個:
$count: 0;
@mixin add {
$count: $count + 1 !global;
}
.a { @include add; }
.b { @include add; } // 此時 $count 是 2!global 不能用來宣告新變數
Dart Sass 要求目標變數必須 已經存在於全域,否則報錯。要先在檔案最外層寫好 $count: 0;。
為什麼要少用 !global
它把一段看起來只是「產生樣式」的程式碼,變成會偷偷改變全域狀態的東西:
- 產出結果取決於
@include的 順序,調換兩行樣式就可能改變輸出。 - 從呼叫端
@include add;完全看不出來它動了什麼。 - 模組被載入多次時,累積的結果難以預期。
大多數想用 !global 的情境都有更好的替代:
| 想做的事 | 該用的工具 |
|---|---|
| 依輸入算出一個值 | @function 回傳值 |
| 讓外部設定模組 | !default + @use ... with |
| 累積一組資料 | 用 map 存好,一次處理完 |
| 需要在執行時改變 | CSS 自訂屬性 |
判斷準則
如果拿掉 !global 之後功能就壞了,通常代表這段邏輯的責任畫錯了,把「計算」與「輸出樣式」分開,問題多半就消失了。
常見問題
!default 到底什麼時候才生效?
只有在該變數還沒有值,或現有的值是 null 的時候才會套用。已經被設定成其他值的話,帶 !default 的那一行等於不存在。
沒有 !default 的模組變數可以用 @use with 覆寫嗎?
不行,會直接報錯。@use ... with 只能設定帶有 !default 的變數,因為那是模組作者明確標示為可設定的部分。想讓變數對外開放,就必須加上 !default。
為什麼不建議用 !global?
因為它讓一段區域的程式碼改到全域狀態,後面所有引用該變數的地方都會受影響,而且從呼叫端完全看不出來。需要回傳值就用 @function,需要可設定就用 !default 搭配 @use ... with,兩者都比 !global 好追蹤。
!global 可以拿來宣告新變數嗎?
不行。Dart Sass 要求 !global 只能指派給已經存在的全域變數,對尚未宣告的變數使用會產生錯誤。正確做法是先在檔案最外層宣告好,再於區域中指派。