Kompromisy CSS-in-JS

Zdjęcie Artem Bali

Ostatnio napisałem przegląd CSS-in-JS na wyższym poziomie, głównie mówiąc o problemach, które to podejście próbuje rozwiązać. Autorzy bibliotek rzadko poświęcają czas na opisywanie kompromisów swojego rozwiązania. Czasami dzieje się tak, ponieważ są zbyt stronnicze, a czasem po prostu nie wiedzą, jak użytkownicy stosują to narzędzie. Jest to więc próba opisania dotychczasowych kompromisów. Myślę, że ważne jest, aby wspomnieć, że jestem autorem JSS, więc powinienem zostać uznany za stronniczy.

Wpływ społeczny

Istnieje warstwa ludzi, którzy pracują na platformie internetowej i nie znają żadnego JavaScript. Ci ludzie zarabiają za pisanie HTML i CSS. CSS-in-JS wywarł ogromny wpływ na przepływ pracy programistów. Naprawdę transformacyjna zmiana nigdy nie będzie możliwa bez pozostawienia niektórych ludzi w tyle. Nie wiem, czy CSS-in-JS musi być jedynym sposobem, ale masowa adopcja jest wyraźnym znakiem problemów z używaniem CSS we współczesnych aplikacjach.

Dużą część problemu stanowi nasza niezdolność do dokładnego komunikowania przypadków użycia, w których świeci CSS-in-JS, oraz tego, jak właściwie używać go do zadania. Wielu entuzjastów CSS-in-JS z powodzeniem promowało technologię, ale niewielu krytyków mówiło o kompromisach w konstruktywny sposób, nie robiąc tanich zmian w narzędziach. W rezultacie pozostawiliśmy wiele kompromisów w ukryciu i nie podjęliśmy żadnego wysiłku, aby podać wyjaśnienia i obejścia.

CSS-in-JS to próba ułatwienia obsługi złożonych przypadków użycia, więc nie pchaj go tam, gdzie nie jest potrzebny!

Koszt wykonania

Gdy CSS jest generowany z JavaScript w czasie wykonywania, w przeglądarce występuje nieodłączny narzut. Narzut środowiska wykonawczego różni się w zależności od biblioteki. Jest to dobry ogólny test porównawczy, ale należy wykonać własne testy. Główne różnice w środowisku wykonawczym pojawiają się w zależności od potrzeby pełnego parsowania CSS ciągów szablonów, ilości optymalizacji, szczegółów implementacji stylów dynamicznych, algorytmu mieszania i kosztów integracji frameworka. *

Oprócz potencjalnego obciążenia środowiska wykonawczego należy wziąć pod uwagę 4 różne strategie pakietowania, ponieważ niektóre biblioteki CSS-w-JS obsługują wiele strategii i ich zastosowanie zależy od użytkownika. *

Strategia 1: Tylko generowanie środowiska wykonawczego

Generowanie środowiska wykonawczego CSS to technika, która generuje ciąg CSS w JavaScript, a następnie wstrzykuje ten ciąg przy użyciu znacznika stylu do dokumentu. Ta technika tworzy arkusz stylów, a nie style wbudowane.

Wadą generowania środowiska wykonawczego jest niemożność zapewnienia stylowej zawartości na wczesnym etapie, gdy dokument zaczyna się ładować. Takie podejście zwykle pasuje do aplikacji bez treści, które mogą być przydatne natychmiast. Zazwyczaj takie aplikacje wymagają interakcji użytkownika, zanim naprawdę staną się przydatne dla użytkownika. Często takie aplikacje działają z treściami, które są tak dynamiczne, że stają się przestarzałe, gdy tylko je załadujesz, dlatego musisz na początku ustanowić potok aktualizacji, na przykład Twitter. Ponadto, gdy użytkownik jest zalogowany, nie ma potrzeby dostarczania HTML dla SEO.

Jeśli interakcja wymaga JavaScript, pakiet musi zostać załadowany, zanim aplikacja będzie gotowa. Na przykład możesz wyświetlić zawartość domyślnego kanału podczas ładowania Slacka do dokumentu, ale prawdopodobne jest, że użytkownik będzie chciał potem zmienić kanał. Więc jeśli załadowałeś początkową zawartość, aby natychmiast ją wyrzucić.

Postrzeganą wydajność takich aplikacji można poprawić za pomocą symboli zastępczych i innych sztuczek, aby pozwolić aplikacji poczuć się bardziej natychmiastowo niż jest w rzeczywistości. Takie aplikacje i tak zwykle wymagają dużej ilości danych, więc nie będą przydatne tak szybko, jak artykuł.

Strategia 2: Generowanie środowiska wykonawczego za pomocą Critical CSS

Krytyczny CSS to minimalna ilość CSS wymagana do stylizacji strony w jej początkowym stanie. Jest renderowany przy użyciu znacznika stylu w nagłówku dokumentu. Ta technika jest szeroko stosowana zi bez CSS-in-JS. W obu przypadkach prawdopodobne jest dwukrotne załadowanie reguł CSS, raz jako część Krytycznego CSS i raz jako część pakietu JavaScript lub CSS. Rozmiar Critical CSS może być dość duży w zależności od ilości treści. Zwykle dokument nie jest buforowany.

Bez Critical CSS, jednostronna aplikacja o dużej zawartości i statycznej aplikacji z czasem działania CSS-in-JS będzie musiała wyświetlać symbole zastępcze zamiast treści. Jest to złe, ponieważ może być przydatne dla użytkownika znacznie wcześniej, poprawiając dostępność na urządzeniach klasy niskiej i dla połączeń o niskiej przepustowości.

Dzięki krytycznemu CSS generowanie środowiska wykonawczego CSS można wykonać na późniejszym etapie, bez blokowania interfejsu użytkownika w początkowej fazie. Ostrzegamy jednak, że na słabszych urządzeniach mobilnych, które mają około 5 lat, generowanie CSS z JavaScript może mieć negatywny wpływ na wydajność. Zależy to silnie od ilości generowanego CSS i używanej biblioteki, więc nie można go generalizować.

Kompromisem tej strategii jest koszt krytycznej ekstrakcji CSS i koszt generowania CSS w czasie wykonywania.

Strategia 3: Tylko ekstrakcja czasu kompilacji

Ta strategia jest domyślna w sieci bez CSS-in-JS. Niektóre biblioteki CSS-in-JS pozwalają na wyodrębnianie statycznego CSS w czasie kompilacji. * W takim przypadku nie ma narzutu związanego z uruchomieniem, CSS jest renderowany na stronie za pomocą znacznika link. Koszt generowania CSS jest wypłacany raz z góry.

Istnieją tutaj 2 główne kompromisy:

  1. Nie możesz używać niektórych dynamicznych interfejsów API CSS-in-JS w czasie wykonywania, ponieważ nie masz dostępu do stanu. Często nadal nie można używać niestandardowych właściwości CSS, ponieważ nie są one obsługiwane w każdej przeglądarce i z natury nie mogą być wypełniane w czasie kompilacji. W takim przypadku musisz wykonać obejścia dla dynamicznych motywów i stylów opartych na stanach. *
  2. Bez Krytycznego CSS i pustej pamięci podręcznej zablokujesz pierwszą farbę, dopóki pakiet CSS nie zostanie załadowany. Element linku w nagłówku dokumentu blokuje renderowanie HTML.
  3. Specyfika niedeterministyczna z podziałem pakietów na stronie w aplikacjach jednostronicowych. *

Strategia 4: Ekstrakcja w czasie kompilacji za pomocą Critical CSS

Ta strategia nie jest również unikalna dla CSS-in-JS. Pełna statyczna ekstrakcja z krytycznym CSS zapewnia najlepszą wydajność podczas pracy z bardziej statyczną aplikacją. Podejście to wciąż ma wyżej wymienione wady statycznego CSS, z tym wyjątkiem, że znacznik linku blokującego można przenieść na dół dokumentu.

Istnieją 4 główne strategie renderowania CSS. Tylko 2 z nich są specyficzne dla CSS-in-JS i żadna z nich nie dotyczy wszystkich bibliotek.

Dostępność

CSS-in-JS może zmniejszyć dostępność, jeśli zostanie użyty w niewłaściwy sposób. Stanie się tak, gdy strona w dużej mierze statyczna zostanie zaimplementowana bez krytycznej ekstrakcji CSS, dzięki czemu HTML nie będzie mógł zostać namalowany przed załadowaniem i oceną pakietu JavaScript. Może się to również zdarzyć, gdy ogromny plik CSS jest renderowany przy użyciu blokującego znacznika odsyłacza w nagłówku dokumentu, co jest najpopularniejszym obecnie problemem z tradycyjnym osadzaniem i nie jest specyficzne dla CSS-in-JS.

Programiści muszą wziąć odpowiedzialność za dostępność. Nadal istnieje silna błędna idea, że ​​niestabilne połączenie internetowe jest problemem krajów słabych ekonomicznie. Zazwyczaj zapominamy, że mamy problemy z łącznością każdego dnia, gdy wchodzimy do systemu kolei podziemnej lub dużego budynku. Stabilne bezprzewodowe połączenie mobilne to mit. Stabilne połączenie WiFi nie jest nawet łatwe, na przykład sieć Wi-Fi 2,4 GHz może powodować zakłócenia z kuchenki mikrofalowej!

Koszt krytycznego CSS z renderowaniem po stronie serwera

Aby uzyskać krytyczną ekstrakcję CSS dla CSS-in-JS, potrzebujemy SSR. SSR to proces generowania końcowego kodu HTML dla danego stanu aplikacji na serwerze. W rzeczywistości może to być dość złożony i kosztowny proces. Wymaga pewnej liczby cykli procesora na serwerze dla każdego żądania HTTP.

CSS-in-JS zwykle wykorzystuje fakt, że jest on podłączony do potoku renderowania HTML. * Wie, jaki HTML został wyrenderowany i jakiego CSS potrzebuje, aby był w stanie wyprodukować absolutnie minimalną jego ilość. Krytyczny CSS dodaje dodatkowy narzut do renderowania HTML na serwerze, ponieważ ten CSS również musi zostać skompilowany w końcowy ciąg CSS. Jednak w niektórych sytuacjach buforowanie na serwerze jest trudne lub wręcz niemożliwe.

Renderowanie czarnej skrzynki

Musisz wiedzieć, w jaki sposób używana biblioteka CSS-in-JS renderuje Twój CSS. Na przykład ludzie często nie są świadomi tego, w jaki sposób Stylowane Komponenty i Emocja implementują style dynamiczne. Style dynamiczne to składnia, która umożliwia korzystanie z funkcji JavaScript w deklaracji stylów. Funkcje te akceptują rekwizyty i zwracają blok CSS.

Aby zachować spójność specyficzności kolejności źródłowej, obie wyżej wymienione biblioteki generują nową regułę CSS, jeśli zawiera dynamiczną deklarację, a komponent aktualizuje się o nowe rekwizyty. Aby zademonstrować, co mam na myśli, stworzyłem tę piaskownicę. W JSS zdecydowaliśmy się na inny kompromis, który pozwala nam aktualizować właściwości dynamiczne bez generowania nowych reguł CSS. *

Stroma krzywa uczenia się

Dla osób zaznajomionych z CSS, ale nowymi w JavaScript, początkowa ilość pracy, aby przyśpieszyć pracę z CSS-in-JS, może być dość duża.

Nie musisz być profesjonalnym programistą JavaScript, aby pisać CSS-in-JS, aż do momentu, kiedy zaangażowana zostanie złożona logika. Nie możemy uogólnić złożoności stylizacji, ponieważ tak naprawdę zależy ona od przypadku użycia. W przypadkach, w których CSS-in-JS staje się skomplikowany, jest prawdopodobne, że implementacja z waniliowym CSS byłaby jeszcze bardziej złożona.

W przypadku podstawowego stylu CSS-in-JS trzeba wiedzieć, jak deklarować zmienne, jak używać ciągów szablonów i interpolować wartości JavaScript. Jeśli używana jest notacja obiektowa, trzeba wiedzieć, jak pracować z obiektami JavaScript i składnią obiektową specyficzną dla biblioteki. Jeśli chodzi o styl dynamiczny, trzeba wiedzieć, jak korzystać z funkcji JavaScript i warunków warunkowych.

Ogólnie rzecz biorąc, istnieje krzywa uczenia się, nie możemy temu zaprzeczyć. Ta krzywa uczenia się jest zwykle niewiele większa niż nauka Sassa. W rzeczywistości stworzyłem ten kurs jajogłowy, aby to wykazać.

Brak interoperacyjności

Większość bibliotek CSS-in-JS nie jest interoperacyjnych. Oznacza to, że stylów napisanych przy użyciu jednej biblioteki nie można renderować przy użyciu innej biblioteki. W praktyce oznacza to, że nie można łatwo przełączać całej aplikacji z jednej implementacji na drugą. Oznacza to również, że nie możesz łatwo udostępniać interfejsu użytkownika w NPM bez włączania wybranej biblioteki CSS-in-JS do pakietu konsumenta, chyba że masz ekstrakcję statyczną podczas kompilacji CSS.

Rozpoczęliśmy pracę nad formatem ISTF, który ma rozwiązać ten problem, ale niestety nie mieliśmy jeszcze czasu, aby doprowadzić go do stanu gotowości do produkcji. *

Wydaje mi się, że udostępnianie komponentów agnostycznego interfejsu użytkownika wielokrotnego użytku w domenie publicznej jest nadal trudnym do rozwiązania problemem.

Zagrożenia bezpieczeństwa

Możliwe jest wprowadzenie przecieków bezpieczeństwa za pomocą CSS-in-JS. Podobnie jak w przypadku wszystkich aplikacji klienckich, zawsze musisz uciec od danych wejściowych użytkownika przed ich renderowaniem.

W tym artykule dowiesz się więcej o niektórych przykładach.

Nieczytelne nazwy klas

Niektórzy ludzie nadal uważają, że ważne jest, abyśmy mieli w Internecie czytelne nazwy klas. Obecnie wiele bibliotek CSS-in-JS zapewnia znaczące nazwy klas na podstawie nazwy deklaracji lub nazwy komponentu w trybie programowania. Niektóre z nich pozwalają nawet dostosować funkcję generatora nazw klas.

Jednak w trybie produkcyjnym większość z nich generuje krótsze nazwy dla mniejszego ładunku. Jest to kompromis, który użytkownik biblioteki musi wykonać i dostosować bibliotekę w razie potrzeby.

Wniosek

Kompromisy istnieją i prawdopodobnie nawet nie wspomniałem o nich wszystkich. Ale większość z nich nie dotyczy wszystkich CSS-in-JS. Zależą od używanej biblioteki i sposobu jej używania.

* Wyjaśnienie tego zdania zajmie dedykowany artykuł. Daj mi znać na Twitterze (@ oleg008) o tym, o którym chcesz przeczytać więcej.