Skip to content

Sass 變數旗標:!default 讓變數可被覆寫、!global 的代價

Sass 的 !default 與 !global 變數旗標

變數 宣告的結尾可以再掛一個 旗標,用來改變指派的行為。Sass 有兩個:!default 讓值變成「預設值」,!global 讓區域的指派寫回全域。前者是寫可重用模組的必備工具,後者則是應該盡量繞開的寫法。

!default:沒設定過才生效

scss
$brand: #2965f1 !default;

這一行的意思是:如果 $brand 還沒有值(或值是 null),就設成 #2965f1;已經有值就什麼都不做。

scss
$brand: #e03131;
$brand: #2965f1 !default;

.btn {
  color: $brand; // #e03131,前面已經設定過了
}

順序很重要,帶 !default 的那一行必須在後面,它才有機會判斷「前面是不是已經給過了」。

它真正的用途:可設定的模組

單一檔案裡用 !default 意義不大,它的價值在跨檔案。假設有一個按鈕模組:

scss
// _button.scss
$radius: 4px !default;
$height: 40px !default;

.btn {
  border-radius: $radius;
  height: $height;
}

使用它的人可以在載入時改掉這些值:

scss
// main.scss
@use "button" with (
  $radius: 999px
);

產出的 .btn 圓角變成 999px$height 沒指定就沿用模組自己的 40px

沒有 !default 就不能被設定

@use ... with 只認得帶 !default 的變數,對一般變數使用會直接編譯失敗。所以模組作者要想清楚:哪些變數是對外開放的設定項,就加 !default 詳細規則見 @use

null 也算「還沒設定」

scss
$shadow: null;
$shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !default;

.card {
  box-shadow: $shadow; // 陰影會生效
}

null 被視為沒有值,所以 !default 依然會套用。這讓「先宣告佔位、之後再決定」的寫法變得可行。

!global:把區域指派寫回全域

預設情況下,區塊裡的指派只影響區塊內:

scss
$count: 0;

@mixin add {
  $count: $count + 1; // 只改到區域的副本
}

加上 !global 才會真的改到外層那一個:

scss
$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 只能指派給已經存在的全域變數,對尚未宣告的變數使用會產生錯誤。正確做法是先在檔案最外層宣告好,再於區域中指派。

延伸閱讀