@use
Założenia wstępne
Jeśli czytając ten wpis nie chcesz się czuć zakręcony jak bąk w tulipanie to musisz umieć sprawnie poruszać się w projektach z kilkoma plikami SCSS, które mogą być między sobą importowane i korzystają z syntaxu SASS-a. Dodatkowo - zakladam, że wiesz w jaki sposób kompliować SCSS na CSS.
Intro
Czasami odnoszę wrażenie, że bardzo łatwo przyzwyczaić się do pewnych rozwiązań związanych z SASS-em. Najpierw uczymy się z dokumentacji czegoś nowego, implementujemy to w projekcie, potem w drugim, trzecim, dziesiątym, piętnastym. Struktura naszych projektów jest podobna, style organizujemy podobnie. Życie jest wspaniałe.
Jeśli jakieś zmiany w SASS są wprowadzane, zazwyczaj są dość "delikatne" i łatwe do przetrawienia. Nie czujemy takiej presji jak w przypadku JS-a i związanaj z nim bezustannej gonitwy frameworków i ich updatów. Takie podejście jest dość zgubne i usypia naszą czujność. Prawda jest taka, ze SASS nieustannie się rozwija i czasami można łatwo przegapić zmiany, które są niezwykle pomocne i rozwiązują uciążliwe problemy. Zwłaszcza jeśli pracujemy w projekcie, który ma zdecydowanie za mało styli żeby implementacja SASS-a miała sens albo jeśli customowych styli w projekcie nie ma wcale. W takiej sytuacji nie mamy dodatkowego "przymusu" żeby od czasu do czasu sprawdzić co się zmieniło.
Uświadomiłem to sobie gdy podczas przeglądania dokumentacji Sass-a natknąłem się na poniższy fragment:
"The Sass team discourages the continued use of the @import rule. Sass will gradually phase it out over the next few years, and eventually remove it from the language entirely. Prefer the @use rule instead."
sass-lang.com/documentation/at-rules/import
Pierwsze zderzenie z teraźniejszością
Ze co ? Deprecated ? @import, ktory widnieje w 3/4 moich projektów i repozytoriów ?
Szperasz dalej i dowiadujesz się, żę Twoja ukochana wtyczka do VSC kompilująca SCSS na CSS zaczyna sypać warningami, i straszy Cię informacjami w stylu "@import deprecated ".
Nagle zaczynasz się czuć jakbyś obudził się ze snu zimowego albo jakby Twoi rodzice powiedzieli Ci, że jesteś adoptowany / adoptowana. Zastanawiasz się jak to do cholery możliwe, że Ci to umknęło i masz wrażenie jakbyś przegapił 10 lat rozwoju ludzkości. Znasz to uczucie ? :)
Co więcej, po kilku rozmowach z moimi znajomymi okazało się, że nie jestem odosobnionym przypadkiem i nie tylko ja to przespałem.
@use vs @import
Czemu akurat przyczepili się do starego dobrego @import-a?
Tak się składa, że miał kilka dolegliwości, które nieco utrudniały pracę ze stylami. Oto niektóre z nich, które wyszczególnione w oficjalnej dokumentacji SASS-a:
- Wszystko globalne! Zmienne, mixiny, funkcje. Każdy z tych bajerów automatycznie stawał się globalnie dostępny. Sprawia to, że zlokalizowanie miejsca deklaracji danego kawałka kodu staje się znacznie trudniejsze.
- Przez to, że wszystko jest globalne możemy mieć problemy z
przestrzeniami nazw(namespace) - Nie mamy możliwości zadeklarowania prywatnych właściwości. Przecież nie wszystkim w danym pliku musimy chcieć się dzielić.
@use jest oficjalną alternatywą dla @import-a. Korzystając z niej jesteśmy w stanie wyeliminować powyższe mankamenty.
Importowanie i global scope
Przejdźmy do konkretów. Jak zastąpić @import naszą "nową" zabawką ?
Przeanalizujmy poniższy przykład. Przy okazji zrozumiemy jeszcze problem globalnego scope-u zmiennych w plikach SCSS.
Dostajesz zadanie. Musisz zmienić kolor tła na stronie na czerwony. Tworzysz PR i Twoje zmiany wygladają tak:
$main-red: red;
body {
background-color: $main-red;
}
// main.scssWszystko działa, jednak jeden ze sprawdzających Twój kod zwrócił uwagę, że lepiej wrzucić zmienne z kolorami do osobnego pliku. To nie problem. Zróbmy to.
$main-red: red;
//colors.scssOczywiscie main musi wiedzieć o tej zmiennej z nowego pliku. Zaimportujmy ją.
@import "./colors.scss";
body {
background-color: $main-red;
}
// main.scssTło staje sie czerwone. Po naszym małym refactorze nadal szystko działa sprawnie.
Tygodnie mijają, projekt się rozwija.
Po jakimś czasie chcemy zaimportować kolejny plik do main.scss. Plik ten będzie miał za zadanie dodanie kilku zmian stylistycznych (no shit Sherlock). Powiedzmy, że chcemy zmienić kolor tekstu na stronie na niebieski. Na potrzeby przykładu załóżmy, że w pliku z kolorami colors.scss ktoś dodał dodatkową zmienną z kolorem niebieskim.
body {
color: $main-blue; //używamy zmiennej, do której ten plik teoretycznie nie ma dostępu
}
// fontColor.scssOczywiście nowy plik musimy zaimportować w main.scss
@import "./colors.scss";
@import "./fontColor.scss";
body {
background-color: $main-red;
}
// main.scssUżyliśmy zmiennej $main-blue, którą znaleźliśmy w pliku colors.scss (załóżmy, że tam była). Po chwili zdaliśmy sobie sprawę, że przecież w pliku fontColor.scss nie ma żadnego importu. Jakim cudem zmiana zadziałała i kolor tesktu zmienił sie na niebieski ?
To jest właśnie jeden z problemów, o których wspominałem wcześniej. Globalne zmienne.
Przeanalizujmy jeszcze raz plik main.scss. Mamy tam dwa importy, jeden pod drugim.
Problem polega na tym, że plik fontColor.scss automatycznie ma dostęp do zmiennych z pliku colors.scss. Newet jeśli ich jawnie nie importował. Niby nic wielkiego ale musisz przyznać, że rodzi to pewne zagrożenia i komplikacje, przykładowo związane z konfliktami nazw.
Przestrzenie nazw (namespaces)
Postarajmy sie naprawic ten problem korzystajac z @use
Po pierwsze zmieniamy nasze @import na @use (kapitan oczywisty strikes again)
@use './colors.scss';
@use './fontColor.scss';
body {
background-color: $main-red;
}
// main.scssZapisujemy zmiany. Kompilacja styli zacznie wyrzucać bląd.
Error: Undefined variable: color: $main-blue; fontColor.scss
No tak. To ma sens. Korzystając z @use musimy najpierw zaimportować to czego chcemy użyć. W przypadku pliku fontColor potrzebujemy zawartości pliku colors.scss. Wciągnijmy go.
@use 'colors';
body {
color: $main-blue;
}
// fontColor.scssZrobione. Ale zaraz, to nadal nie działa bo kompilacja cały czas krwawi... O co chodzi ?
Korzystając z @use musimy pamiętać, że nasze zmienne zostają przypisane do konkretnej przestrzeni nazw - namespace-a . Nasz plik nazywa się colors.scss więc jego namespacem w tym przypadku będzie colors
@use 'colors';
body {
color: colors.$main-blue;
}
// fontColor.scssZapisujemy. Nareszcie plik fontColor.scss nie wyrzuca żadnych błędow. Teraz analogicznie musimy poprawić main.scss
@use './colors.scss';
@use './fontColor.scss';
body {
background-color: colors.$main-red;
}
// main.scssUdało się. Kompilacja zielona. Brak kolejnych blędow. Strona wygląda tak jak powinna.
Custom namespace
Co jeśli z jakiegoś powodu chcemy mieć kompletnie inny namespace ? Czy jesteśmy uwiązani z nazwą pliku ? Na szczęście nie. SASS umożliwia nam stworzenie customowej przestrzeni nazw. Wygląda to mniej więcej w ten sposob:
@use './colors.scss' as kolorki; // nowy namespace
body {
background-color: kolorki.$main-red; // nowy namespace w akcji !
}Możliwości konfiguracyjne
Kolejną przydatną ciekawostką płynącą z @use jest możliwość importowania pliku z dodatkowymi parametrami. Najlepiej zrozumieć to patrząc na przykład. Wróćmy na moment do pliku colors.scss
$main-red: red !default;
$main-blue: blue !default;
// colors.scssCo się zmieniło ? Jak już zapewne zauważyłeś, dodałem do każdej wartości dodatkową flagę !default. Tutaj zaczyna się zabawa. Korzystając z tej flagi mówimy "Chciałbym być w stanie zmienić te wartości podczas importowania mojego modułu ze stylami!"
W jaki sposób tego użyć? Jest to bardzo proste.
@use './colors.scss' with (
$main-red: teal,
$main-blue: orangered
);
// main.scssWiem, ze powyższy zapis może się wydawać dziwny ale jego działanie jest bardzo proste.
Pamietasz flagi !default z poprzedniego pliku ? One pozwliły nam zmienić wartość podczas importu. Dokladnie to robimy w powyższym snippecie.
Kolor zmiennej $main-red chcemy zmienić na teal
Kolor zmiennej $main-blue chcemy zmienić na orangered
Kod kompiluje się bez problemu. Kolory zostaly zamienione na teal i orangered.
Co w przypadku, gdy mamy dodaną flagę !default do kilku wartości ale podczas importu nie chcemy nadpisac jednej z nich?
To rownież jest banalnie proste - wystarczy ją olać podczas importu modułu. Wówczas zastosowana zostanie domyślna wartość z pliku colors.scss widniejąca przed flagą !default. Rozważmy poniższy przykład:
@use './colors.scss' with (
$main-red: teal,
);
// main.scssNadpisujemy wartość zmiennej main-red wartoscią teal (teal to taki zielonopodobny kolor). Main-blue rownież moglibyśmy nadpisać ale tego nie zrobilismy. W tym przypadku - jak już wspomniałem, zastosowany zostanie kolor widniejący obok flagi !default.
Innymi słowy - $main-red: red !default; można rozumieć jako -> "Jesli podczas importu nie zostanie podana inna wartość - defaultuj do red"
Prywatne właściwości
Co jeśli mamy plik z różnymi zmiennymi ale nie chcemy udostępniać niektórych na zewnątrz ? Co jeśli jakaś zmienna jest przydatna tylko i wyłącznie w jednym pliku i inne jej nie potrzebują?
@use rozwiazuje rownież ten problem. Konfiguracja jest bardzo skomplikowana więc się skup.
Przed nazwą zmiennej dodaj - lub _
Już. To wszystko : )
$-main-red: red; // prywatna zmienna
// colors.scss@use './colors.scss';
body {
background-color: colors.$-main-red;
}
// main.scssRezultat: Error: Private members can't be accessed from outside their modules.
Podsumowanie
@use jest zdecydowanie dobrym krokiem w kierunku rozwoju SASS-a. Nie dość, że rozwiązuje on dość istotne problemy, z którymi borykaliśmy się do tej pory to jeszcze jest bardzo prosty w implementacji i myślę, że migracja mniejszych (pod kątem styli) aplikacji nie powinna być bolesna.
Być może warto sprawdzić narzędzie do migracji ?
sass-lang.com/documentation/cli/migrator
Źródła
youtube.com/watch?v=CR-a8upNjJ0