SASS 中的繼承
本文將說明 SASS 中的繼承。
我們將用實際範例說明 SASS 的繼承。
YouTube Video
SASS 中的繼承
SASS 中的繼承(@extend)是一種機制,可以讓你將一個選擇器的樣式套用到另一個選擇器上,而不需重複撰寫樣式。由於相同樣式會被「合併」並輸出給標記中的多個元素,因此產生的 CSS 不易冗餘;但如果使用不當,可能會導致選擇器的意外合併。
基礎:如何使用 @extend
以下是一個基礎範例,.btn--primary 繼承了 .btn 的樣式。@extend 是用來擴展目標選擇器的指令。
1// Base button styles
2.btn {
3 padding: 0.5rem 1rem;
4 border-radius: 4px;
5 border: 1px solid #ccc;
6 background: white;
7 color: #333;
8}
9
10/* Primary button extends the base .btn */
11.btn--primary {
12 @extend .btn;
13 background: #007bff;
14 color: white;
15}- 使用
@extend,.btn--primary可以繼承.btn的基礎樣式,只覆蓋必要的部分。
產生的 CSS
1.btn, .btn--primary {
2 padding: 0.5rem 1rem;
3 border-radius: 4px;
4 border: 1px solid #ccc;
5 background: white;
6 color: #333;
7}
8
9.btn--primary {
10 background: #007bff;
11 color: white;
12}最佳實踐:使用佔位選擇器(%placeholder)
占位符選擇器(%name)是指不會被輸出到CSS中的選擇器。當你希望在多個元件之間安全共享僅用於繼承的共用樣式時,佔位符選擇器會被廣泛使用。
1// %placeholder will not be output directly
2%card-base {
3 padding: 1rem;
4 border-radius: 6px;
5 box-shadow: 0 1px 3px rgba(0,0,0,0.08);
6 background: #fff;
7}
8
9/* Components extend the placeholder */
10.card {
11 @extend %card-base;
12 border: 1px solid #eee;
13}
14
15.panel {
16 @extend %card-base;
17 border: 1px solid #ddd;
18}.card和.panel都從%card-base繼承,因此能共用基礎樣式並可根據需要加以擴充。
產生的 CSS
1.card, .panel {
2 padding: 1rem;
3 border-radius: 6px;
4 box-shadow: 0 1px 3px rgba(0,0,0,0.08);
5 background: #fff;
6}
7
8.card {
9 border: 1px solid #eee;
10}
11
12.panel {
13 border: 1px solid #ddd;
14}多重繼承(多個 @extend)
你可以同時繼承多個佔位符或類別。雖然樣式的可重用性得到了提升,但追蹤哪些規則與哪些選擇器組合起來也非常重要。
1%btn-base {
2 display: inline-block;
3 padding: 0.5rem 1rem;
4 border-radius: 3px;
5}
6
7%btn-large {
8 padding: 0.75rem 1.5rem;
9 font-size: 1.125rem;
10}
11
12/* Composite button that extends both placeholders */
13.btn--lg {
14 @extend %btn-base;
15 @extend %btn-large;
16 background: #222;
17 color: #fff;
18}- 以下是一個按鈕同時繼承兩個佔位符(基本樣式和尺寸樣式)的例子。
.btn--lg同時繼承%btn-base及%btn-large,將基本佈局和大尺寸結合起來。
產生的 CSS
1.btn--lg {
2 display: inline-block;
3 /* %btn-large overrides the padding from %btn-base */
4 padding: 0.75rem 1.5rem;
5 border-radius: 3px;
6 font-size: 1.125rem;
7 background: #222;
8 color: #fff;
9}@extend 的行為(合併機制)及『選擇器爆炸』的注意事項
@extend 會合併所有匹配的選擇器,有時會產生不預期的選擇器組合。
以下範例說明了同一個基礎類別在多個地方被擴展時,產出內容會暴增。
1/* Many components extend .utility */
2/* A generic utility class */
3.utility {
4 margin-bottom: 1rem;
5}
6
7/* Nested selectors that extend .utility */
8.header {
9 @extend .utility;
10 .title {
11 font-weight: bold;
12 }
13}
14
15.footer {
16 @extend .utility;
17 .note {
18 color: #888;
19 }
20}
21
22.article {
23 @extend .utility;
24 .content {
25 line-height: 1.6;
26 }
27}
28
29.sidebar {
30 @extend .utility;
31 .section {
32 padding: 1rem;
33 }
34}- 當多個元件都繼承
.utility時,選擇器會合併成一個,在大型專案內可能導致 CSS 過度膨脹。
產生的 CSS
1.utility,
2.header,
3.footer,
4.article,
5.sidebar {
6 margin-bottom: 1rem;
7}
8
9.header .title {
10 font-weight: bold;
11}
12
13.footer .note {
14 color: #888;
15}
16
17.article .content {
18 line-height: 1.6;
19}
20
21.sidebar .section {
22 padding: 1rem;
23}@extend 及 .class vs. 元素選擇器(標籤)——優先級與副作用
@extend 不僅可以用於類別,也可以用於元素選擇器。然而,擴展元素選擇器會增加影響範圍,導致規則可能被意外應用到不希望的位置。
以下是一個擴展元素選擇器的例子,以及可能帶來的影響。
1/* Extending an element selector (not recommended) */
2h1 {
3 font-size: 2rem;
4 margin-bottom: 0.5rem;
5}
6
7/* If you extend h1, the resulting selector will include your class with h1 */
8.title {
9 @extend h1;
10 color: #333;
11}
12
13/* Output becomes:
14h1, .title { font-size: 2rem; margin-bottom: 0.5rem; }
15*/- 在此例中,繼承元素選擇器
h1使得.title與h1擁有相同的樣式。 - 在小型專案中這看似方便,但隨著專案規模擴大,像是
h1的規則可能會和.title意外結合,使樣式變得更複雜並降低可維護性。因此,主要以類別和佔位符為核心設計樣式會讓維護更容易。
產生的 CSS
1h1,
2.title {
3 font-size: 2rem;
4 margin-bottom: 0.5rem;
5}
6
7.title {
8 color: #333;
9}@extend 與 !optional 的應用場合
若在 @extend 後加上 !optional,當繼承目標不存在時可以抑制錯誤訊息。這在類似程式庫的程式碼或根據條件定義佔位符的情境下特別有用。
以下是一個安全地嘗試繼承可能不存在的類別、並使用 !optional 的範例。
1/* Try to extend a class that might not exist */
2.component {
3 @extend .maybe-existing !optional;
4 padding: 1rem;
5}- 如果
.maybe-existing不存在,什麼都不會發生,並會被略過。當你需要安全地嘗試繼承時可以使用此語法。
產生的 CSS
1.component {
2 padding: 1rem;
3}@extend 與混入(@mixin / @include)的比較
@extend 與 @mixin 在某些情境下用途重疊,但輸出結果和適用場合不同。
-
@extend- 產生的 CSS 會通過合併選擇器減少冗餘。
- 由於選擇器是在產生後合併,因此可能出現非預期的組合。
- 無法傳遞參數(但可透過結合佔位符來補足)。
-
@mixin/@include- 每次呼叫都會重複產生樣式(導致冗餘輸出)。
- 可傳遞參數並包含條件式、迴圈等邏輯。
- 輸出較可預期,但檔案體積會增加。
以下對比用 @mixin 與 @extend 實現相同按鈕樣式的範例。
1/* Mixin approach */
2@mixin btn-styles($bg, $color) {
3 display: inline-block;
4 padding: 0.5rem 1rem;
5 background: $bg;
6 color: $color;
7 border-radius: 4px;
8}
9
10/* Use mixin */
11.btn {
12 @include btn-styles(white, #333);
13}
14
15.btn--primary {
16 @include btn-styles(#007bff, white);
17}
18
19/* Extend approach (shared placeholder) */
20%btn-base {
21 display: inline-block;
22 padding: 0.5rem 1rem;
23 border-radius: 4px;
24}
25
26.btn2 {
27 @extend %btn-base;
28 background: white;
29 color: #333;
30}
31
32.btn2--primary {
33 @extend %btn-base;
34 background: #007bff;
35 color: white;
36}@mixin可靈活插入樣式,@extend則能高效合併輸出,可依情境選擇使用。
產生的 CSS
@mixin 的輸出
1.btn {
2 display: inline-block;
3 padding: 0.5rem 1rem;
4 background: white;
5 color: #333;
6 border-radius: 4px;
7}
8
9.btn--primary {
10 display: inline-block;
11 padding: 0.5rem 1rem;
12 background: #007bff;
13 color: white;
14 border-radius: 4px;
15}@extend 的輸出
1.btn2,
2.btn2--primary {
3 display: inline-block;
4 padding: 0.5rem 1rem;
5 border-radius: 4px;
6}
7
8.btn2 {
9 background: white;
10 color: #333;
11}
12
13.btn2--primary {
14 background: #007bff;
15 color: white;
16}實務指引
SASS 的繼承功能可以大幅提升樣式的重用性,是一個強大的功能。但若誤用,可能讓樣式合併變得複雜且降低維護性。以下是安全且高效使用繼承的幾個重點。
- 對於純粹共用的元件樣式,例如結構和版面配置,請使用佔位符。另外,如果需要動態參數化,可以使用
@mixin。 - 應避免直接繼承像是
h1這樣的 HTML 元素。可能會出現非預期的選擇器組合,導致產生意料之外的 CSS。 - 採用 BEM 等命名規範或明確前綴標識每個佔位符的用途,有助於保持安全。
- 在同一個檔案內使用
@extend會比較安全。特別是在大型專案中,建議在每個元件的作用範圍內設計繼承,這有助於追蹤繼承關係。
總結
SASS 的 @extend 功能是一種高效重複利用共用樣式並確保設計一致性的便利方法。然而,選擇器組合很容易變得複雜,因此必須謹慎且限縮地使用這個功能。將共用樣式以佔位符選擇器(%placeholder)進行分組,並用 @mixin 處理需要動態參數的部分,可以維持簡單且易於維護的設計。
您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。