Dziedziczenie w SASS

Dziedziczenie w SASS

Ten artykuł wyjaśnia dziedziczenie w SASS.

Wyjaśnimy dziedziczenie w SASS na praktycznych przykładach.

YouTube Video

Dziedziczenie w SASS

Dziedziczenie w SASS (@extend) to mechanizm pozwalający zastosować style jednego selektora do innego bez duplikacji. Ponieważ te same style są ‘łączone’ i generowane dla wielu elementów w kodzie, wynikowy CSS jest mniej podatny na zbędne powtórzenia; jednakże, przy niewłaściwym użyciu, może prowadzić do niezamierzonych połączeń selektorów.

Podstawy: jak używać @extend

Poniżej znajduje się podstawowy przykład, gdzie .btn--primary dziedziczy style po .btn. @extend to dyrektywa, która rozszerza wskazany selektor.

 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}
  • Dzięki zastosowaniu @extend, .btn--primary dziedziczy bazowe style z .btn, nadpisując tylko te części, które są potrzebne.

Wygenerowany 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}

Najlepsza praktyka: używanie placeholderów (%placeholder)

Selektory zastępcze (%name) to selektory, które nie są generowane w CSS. Są szeroko stosowane szczególnie wtedy, gdy chcesz bezpiecznie dzielić wspólne style wyłącznie do dziedziczenia pomiędzy wieloma komponentami.

 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 i .panel dziedziczą po %card-base, co pozwala im dzielić wspólne style oraz dodawać różnice w razie potrzeby.

Wygenerowany 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}

Wielokrotne dziedziczenie (wiele @extend)

Możesz dziedziczyć po wielu selektorach placeholder lub klasach jednocześnie. Choć ponowne użycie stylów zostaje zwiększone, ważne jest, aby śledzić, które reguły są łączone z którymi selektorami.

 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}
  • To przykład, gdzie przycisk dziedziczy dwa placeholdery – jeden dla 'bazowych' stylów i drugi dla 'rozmiaru'.
  • .btn--lg dziedziczy zarówno %btn-base, jak i %btn-large, łącząc bazowy układ z większym rozmiarem.

Wygenerowany 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}

Zachowanie @extend (mechanizm łączenia) i ostrzeżenie przed 'eksplozją selektorów'

@extend łączy wszystkie pasujące selektory razem, co czasem może prowadzić do niezamierzonych kombinacji selektorów.

Poniższy przykład pokazuje, jak wynikowy kod może się powiększyć, gdy ta sama klasa bazowa jest rozszerzana w wielu miejscach.

 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}
  • Kiedy wiele komponentów dziedziczy po .utility, selektory są łączone w jeden, a przy dużych projektach może to spowodować rozrost CSS.

Wygenerowany 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 i .class vs selektory elementów (tagi) — priorytet i skutki uboczne

@extend można stosować nie tylko do klas, ale również do selektorów elementów. Jednak rozszerzanie elementów zwiększa zakres działania, co podnosi ryzyko niezamierzonego stosowania reguł w nieprzewidzianych miejscach.

Poniżej znajduje się przykład rozszerzania selektora elementu i konsekwencji tego działania.

 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*/
  • W tym przykładzie, dziedzicząc selektor elementu h1, .title zostaje połączone z tymi samymi stylami co h1.
  • Chociaż może się to wydawać wygodne w niewielkich projektach, w miarę rozrastania się projektu reguły dla h1 mogą się niespodziewanie łączyć z .title, czyniąc style bardziej złożonymi i trudniejszymi do utrzymania. Dlatego projektowanie stylów głównie w oparciu o klasy i selektory zastępcze ułatwia ich utrzymanie.

Wygenerowany CSS

1h1,
2.title {
3  font-size: 2rem;
4  margin-bottom: 0.5rem;
5}
6
7.title {
8  color: #333;
9}

Przypadki użycia @extend i !optional

Jeśli określisz !optional wraz z @extend, możesz wyeliminować błędy w przypadku braku celu dziedziczenia. Jest to szczególnie przydatne w kodzie przypominającym bibliotekę lub w przypadkach, gdy selektory zastępcze są definiowane warunkowo.

Poniżej przykład bezpiecznej próby dziedziczenia po klasie, która może nie istnieć, z użyciem !optional.

1/* Try to extend a class that might not exist */
2.component {
3  @extend .maybe-existing !optional;
4  padding: 1rem;
5}
  • Jeśli .maybe-existing nie istnieje, nic się nie dzieje i zostaje to pominięte. Możesz tego użyć, gdy chcesz bezpiecznie spróbować rozszerzenia.

Wygenerowany CSS

1.component {
2  padding: 1rem;
3}

Porównanie @extend i mixinów (@mixin / @include)

@extend i @mixin mają czasami nakładające się funkcje, ale ich wynikowy kod i zastosowania się różnią.

  • @extend

    • Wygenerowany CSS ogranicza powielanie poprzez łączenie selektorów.
    • Ponieważ selektory są łączone podczas generowania, mogą pojawić się niezamierzone kombinacje.
    • Nie można przekazywać parametrów (choć można to obejść, łącząc różne placeholdery).
  • @mixin / @include

    • Każde wywołanie duplikuje style, powodując powstanie zbędnego kodu.
    • Możesz przekazywać parametry i stosować logikę typu instrukcje warunkowe czy pętle.
    • Wynik jest bardziej przewidywalny, ale rozmiar pliku rośnie.

Poniżej znajduje się porównanie użycia zarówno @mixin, jak i @extend do realizacji tych samych stylów przycisku.

 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 pozwala elastycznie dołączać style, a @extend efektywnie konsoliduje wynik; możesz więc stosować oba podejścia w zależności od potrzeby.

Wygenerowany CSS

Wynik z @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}
Wynik z @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}

Praktyczne wytyczne

Dziedziczenie w SASS jest potężną funkcją zwiększającą ponowne użycie stylów. Jednak błędne użycie może skomplikować łączenie stylów i upośledzić możliwość łatwego utrzymania kodu. Poniżej przedstawiono kluczowe wskazówki dotyczące bezpiecznego i efektywnego korzystania z dziedziczenia.

  • Używaj selektorów zastępczych dla czysto wspólnych stylów komponentów, takich jak struktura i układ. Dodatkowo, jeśli wymagana jest dynamiczna parametryzacja, możesz użyć @mixin.
  • Należy unikać bezpośredniego dziedziczenia elementów HTML, takich jak h1. Mogą wystąpić niezamierzone połączenia selektorów, co może skutkować wygenerowaniem nieoczekiwanego CSS.
  • Używanie konwencji nazewniczych, takich jak BEM lub jasne prefiksy wskazujące przeznaczenie placeholderów, pomaga zachować bezpieczeństwo.
  • Bezpieczniej jest używać @extend w ramach tego samego pliku. Szczególnie w dużych projektach zaleca się projektowanie dziedziczenia w obrębie każdego komponentu, aby łatwiej śledzić relacje dziedziczenia.

Podsumowanie

Funkcja @extend w SASS to wygodny sposób na efektywne ponowne wykorzystanie wspólnych stylów i zapewnienie spójności projektu. Jednak kombinacje selektorów mogą łatwo stać się złożone, dlatego należy korzystać z tej funkcji ostrożnie i w ograniczonym zakresie. Grupując wspólne style za pomocą selektorów zastępczych (%placeholder) oraz stosując @mixin do części wymagających dynamicznych parametrów, możesz zachować prosty i łatwy w utrzymaniu projekt.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video