Nasza strona u偶ywa cookies. Dowiedz si臋 wi臋cej o celu ich u偶ywania i zmianie ustawie艅 w przegl膮darce. Korzystaj膮c ze strony, wyra偶asz zgod臋 na u偶ywanie cookies, zgodnie z aktualnymi ustawieniami przegl膮darki. Rozumiem

馃拝 Styled-Components: czy warto je wypr贸bowa膰?

Czyli alternatywny spos贸b nadawania styl贸w aplikacji React
17 04 2018 %281%29

Rozpoczyna艂em ostatnio nowy projekt i w kt贸rym艣 momencie musia艂em zastanowi膰 si臋 nad podej艣ciem do nadawania styl贸w. Pewnie, mog艂em korzysta膰 z tradycyjnego BEM, ale sk艂ania艂em si臋 ku modu艂om CSS, kt贸re dobrze sprawdzaj膮 si臋 z Reactem. Siada艂em ju偶 do pracy, gdy wsp贸艂pracownik zapyta艂, czy my艣la艂em o bibliotece styled-components.

Pocz膮tkowo podszed艂em do tego sceptycznie. Na og贸艂 pisanie styl贸w w JS nie przychodzi mi swobodnie, jednak czasem trzeba spr贸bowa膰 czego艣 nowego. 聽

Styled-components s膮 popularne na GitHubie (w momencie pisania: 15.5k) i w niekt贸rych projektach聽u偶ywane na produkcji. Postanowi艂em spr贸bowa膰, nie zastanawiaj膮c si臋 d艂u偶ej.聽

Czym s膮 styled-components?

Za艂贸偶my, 偶e stawiasz pierwsze kroki w CSS i s艂ysza艂e艣 o nowej, popularnej bibliotece - styled-components. Po kr贸tkim researchu dowiesz si臋, 偶e to jeszcze jedno podej艣cie do stylowania w CSS w JavaScript. Biblioteka ta powsta艂a z my艣l膮 o React, ale pomys艂 znalaz艂 zastosowanie - cho膰 ju偶 nie tak popularne - w vue.js, React Native, a nawet w tradycyjnym DOM.

Mo偶emy ju偶 przej艣膰 do sedna: czym s膮 styled-components? Jakim trzem najwi臋kszym problemom pr贸buj膮 zaradzi膰?


Tworzenie pierwszego komponentu

Styled-components s膮 generowane w locie, dlatego nie musimy modyfikowa膰 konfiguracji u偶ywanego przez nas bundlera. Yeah! Wystarczy, 偶e uruchomimy npm install styled-components i zaimportujemy do komponentu. Przyjrzyjmy si臋 temu na rzeczywistym przyk艂adzie:

import styled from 'styled-components';

const HeaderText = styled.h1`
  font-size: ${ props => props.large ? 32 : 24 }px;
  color: wheat;
  text-transform: uppercase;
`;

export default () => (
  <HeaderText large>Looks Cool</HeaderText>
);

Podstawowe zastosowanie styled-components


Stworzyli艣my tutaj komponent, kt贸ry renderuje聽HeaderText - stylowany element h1. Je艣li przeka偶emy props聽large, przycisk zostanie wyrenderowany z wi臋kszym rozmiarem czcionki. Standardowo - np. w BEM - musieliby艣my napisa膰 dwie klasy i zastosowa膰 je warunkowo.

Sp贸jrzmy na ten sam przypadek, ale w tradycyjnym podej艣ciu:

.header__text {
  font-size: 24px;
  color: wheat;
  text-transform: uppercase;
  
  &--large {
    font-size: 32px;
  }
}

Stylowanie dla komponentu HeaderText

import './HeaderText.scss';

export default ({ 
  large,
  children,
  className,
  ...props
}) => {
  const classList = `header__text ${large && 'header__text--large'} ${className}`;
  
  return (
    <h1 className={classList} {...props}>{children}</h1>
  );
};


Drugi przyk艂ad jest oczywi艣cie bardziej z艂o偶ony. Musimy pami臋ta膰 o przekazywaniu propa聽className, ale r贸wnie偶 wszystkich innych - jak props贸w onClick, czy innych zdarze艅.

Ca艂a funkcjonalno艣膰 styled-components opiera si臋 na raczej niepopularnej i ma艂o znanej funkcji ES6 - Tagged Template Literals. Aby zademonstrowa膰 ich dzia艂anie, stworzymy prost膮 funkcj臋 log. Zaloguje ona wszystkie przekazane argumenty do konsoli i u偶yje ich w dw贸ch r贸偶nych scenariuszach.

const log = (...args) => console.log(...args);

log('This', 'is', 'useless');
// 'This', 'is', 'useless'

const name = 'Bob';
log`Hi ${name}! How are you?`;
// ['Hi ', '! How are you?'], 'Bob'

Funkcja, kt贸ra loguje wszystkie przekazane do niej propsy


Je偶eli wywo艂amy j膮 jako standardow膮 funkcj臋, po prostu wyrzuci wszystkie przekazane do niej argumenty, jeden po drugim. Natomiast je偶eli wywo艂amy t臋 funkcj臋 u偶ywaj膮c tagged template literals to jako pierwsz膮 warto艣膰 otrzymamy tablic臋 wszystkich ci膮g贸w znak贸w, kt贸re by艂y w literale szablonu. Nast臋pnie zwr贸cone zostan膮 wszystkie zmienne i funkcje, kt贸re przekazali艣my do interpolacji.

Je艣li chcesz wiedzie膰 wi臋cej o Tagged Template Literals, sprawd藕 dokumentacj臋 MDN lub przeczytaj artyku艂 napisany przez jednego z tw贸rc贸w biblioteki styled-components, Maxa Stoibera.

Jakie problemy rozwi膮zuj膮 styled-components?


Enkapsulacja styl贸w

W przesz艂o艣ci (znowu nie tak dalekiej, ale hej - m贸wimy o JS鈥), aby utrzyma膰 przejrzyste i gotowe do wielokrotnego u偶ytku style, trzeba by艂o skorzysta膰 z jednej z popularnych metodyk - na czele z BEM - ale mieli艣my te偶 OOCSS, SMACSS, a nawet Atomic CSS. Wszystkie wci膮偶 s膮 trafne i niezwykle przydatne, ale pojawi艂y si臋 nowe mo偶liwo艣ci rozwi膮zania kwestii zasi臋gu styl贸w.

Modu艂y CSS pozwalaj膮 na import plik贸w CSS lub SCSS do JS i odwo艂ywanie si臋 do nazw klas jako kluczy w zaimportowanym obiekcie. To mo偶e wydawa膰 si臋 skomplikowane, ale tak naprawd臋 sprowadza si臋 do 艂膮czenia naszych prostych nazw klas - jak .button - z jakim艣 losowym cz艂onem - np. .button_sRgs42. Dzi臋ki temu mo偶emy korzysta膰 z dobrodziejstw lokalnego zasi臋gu. Styled-components generuj膮 te偶 losowe stringi dla nazw klas, ale pot臋ga tkwi w tym, 偶e dzieje si臋 to automagicznie, bez potrzeby importowania arkuszy styl贸w - co przywodzi na my艣l drugi problem.


艁膮czenie styl贸w i komponent贸w

W przypadku styled-components kiedy chcemy ostylowa膰 element najpierw go tworzymy, a p贸藕niej stylujemy. Biblioteka tworzy automatycznie losowy ci膮g znak贸w i przypisuje wygenerowan膮 nazw臋 klasy do Reacta, vue.js, DOM lub innego komponentu. 聽


Dynamiczne zmiany styl贸w

Jako 偶e piszemy nasze style w JavaScript mo偶emy je dynamicznie zmienia膰. Zatem, zamiast tworzy膰 dwie r贸偶ne klasy dla przycisk贸w .default聽i .disabled, mo偶emy zastosowa膰 w naszych stylach operator warunkowy, kt贸ry b臋dzie wygl膮da艂 mniej wi臋cej tak: color: ${disabled ? 'grey' : 'black'};.

${} jest jeszcze jedn膮 funkcjonalno艣ci膮 ES6, nazywan膮 interpolacj膮 string贸w. Mo偶esz dowiedzie膰 si臋 o niej wi臋cej na MDN.

Za kulisami

Przyjrzymy si臋 wewn臋trznej logice styled-components, aby lepiej zrozumie膰 z czym tak naprawd臋 mamy do czynienia. Co si臋 dzieje, gdy tworzymy ostylowany komponent? Przejd藕my przez to krok po kroku.

const Button = styled.button`
  font-size: 12px;
  background-color: ${props => props.solid ? 'red' : 'blue' };
`;


Na pocz膮tku okre艣lamy, 偶e chcemy stworzy膰 komponent z elementu button. Styled-components tworz膮 nowy komponent React. Gdy zostanie wywo艂ana funkcja componentWillMount聽zweryfikuje ona czy komponent zmieni si臋 dynamicznie, bazuj膮c na props. Je偶eli nie, silnik nie b臋dzie pr贸bowa艂 ponownie wygenerowa膰 styl贸w. Podobnie dzieje si臋 w przypadku componentWillReceiveProps. Je偶eli nast臋puje dynamiczna zmiana, silnik ponownie wylicza style przez wywo艂anie funkcji setState, w kt贸rej wykonywane s膮 potrzebne wyliczenia. Kiedy zostanie wywo艂ane componentWillUnmount聽nast膮pi zwyk艂e odpi臋cie obserwator贸w, by nie przelicza膰 nieu偶ywanych styl贸w.

Kiedy renderujemy komponent silnik bierze tablic臋 string贸w i wszystkie argumenty zwr贸cone z tagged template literal. Pozostaj膮c przy powy偶szym przyk艂adzie z komponentem button, argumenty b臋d膮 wygl膮da艂y nast臋puj膮co:

(
  [ 'font-size: 12px ;background-color: ', ';' ], // first argument
  (props => props.solid ? 'red' : 'blue')
)


Jak zauwa偶yli艣my wcze艣niej, pierwszy argument jest tablic膮 wszystkich string贸w w nawiasach klamrowych, a nast臋pne argumenty (w powy偶szym przyk艂adzie jest tylko jeden) s膮 zmiennymi/funkcjami przekazanymi do interpolacji.

Wtedy komponent sprawdza, czy kt贸ry艣 z argument贸w jest funkcj膮. Je艣li tak, stosuje t臋 funkcj臋 do elementu z tablicy z odpowiednim id (pierwsza funkcja odnosi si臋 do elementu z indeksem 0). Nast臋pnie przekazuje props z komponentu do tej funkcji jako argumenty. Do艂膮cza wynik funkcji do wybranego stringa. Na koniec 艂膮czy wszystkie stringi i wstrzykuje je do tagu head na naszej stronie.

'font-size: 12px; background-color: ' + 'red' + ';'


To du偶e uproszczenie, ale pokazuje g艂贸wny mechanizm stoj膮cy za styled-components. Pozwala na pisanie prawdziwego CSS i wspiera 100% jego funkcjonalno艣ci, co nie pojawia艂o si臋 wcze艣niej (lub nie by艂o na tyle popularne).

Zamieszczam poni偶ej kr贸tki przyk艂ad, kt贸ry odwzorowuje zachowanie styled-components (bez faktycznego wstrzykiwania styl贸w; tworz臋 tylko string CSS).

// component props mock
const properties = {
  color: 'red',
  large: true
};

// curried function
const styled = (props) => (strings, ...args) => {
  return strings.map((string, i) => {
    const correspondingItem = args[i];
  
    switch(typeof correspondingItem) {
      case 'function': 
        return string + correspondingItem(props);
      case 'undefined':
        return string;
      default:
        return string + correspondingItem;
    }
  }).join('');
}

const styles = styled(properties)`
  width: 10px;
  height: 10px;
  background-color: ${props => props.color};
  font-size: ${props => props.large ? 20 : 14}px;
  color: white;
`;

console.log(styles);
/*
"
  width: 10px;
  height: 10px;
  background-color: red;
  font-size: 20px;
  color: white;
"
*/


Wstrzykiwanie styl贸w

Od wersji 3.1.0 wypuszczonej pod koniec stycznia 2018, styled-components korzystaj膮 na produkcji z mniej znanego insertRule API. Ta ma艂a zmiana pozwala na skr贸cenie czasu 艂adowania strony (do stanu interaktywnego - Time-To-First-Interactive) w wi臋kszo艣ci aplikacji. W testach obci膮偶eniowych biblioteki (co nie jest implementacj膮 jak膮 spotykamy na co dzie艅) poskutkowa艂o to 10x-20x szybszym 艂adowaniem. Dzi臋ki usuni臋ciu tego w膮skiego gard艂a styled-components zaczynaj膮 pasowa膰 r贸wnie偶 do wi臋kszych aplikacji, a nie, jak do tej pory, tylko do pobocznych projekt贸w.

Narz臋dzia

Powsta艂o kilka narz臋dzi, kt贸re maj膮 u艂atwi膰 u偶ywanie styled-components, a tym najbardziej przydatnym jak do tej pory jest babel plugin. Umo偶liwia stworzenie konfiguracji, kt贸ra naprawd臋 pomaga w dewelopmencie (jak np. server side rendering) i umo偶liwia zastosowanie 艂adnych nazw klas, kt贸re u艂atwiaj膮 debugowanie. Dzi臋ki niemu mo偶emy okre艣li膰 te偶 opcje do preprocessingu i minifikacji.

Problemy

Styled-components nie s膮 (jeszcze) bezb艂臋dne - napotka艂em wiele mniejszych i wi臋kszych problem贸w. Niekt贸re z nich wkr贸tce zostan膮 rozwi膮zane, bo projekt ma wielu wsp贸艂autor贸w i aktywnie si臋 rozwija. Oto, co powiniene艣 wiedzie膰 zanim rozpoczniesz prac臋 ze styled-components.

  • Wyst臋puj膮 problemy z wydajno艣ci膮, kt贸re trzeba zrozumie膰, aby unikn膮膰 niepotrzebnego ponownego renderowania komponent贸w i s艂abego dzia艂ania animacji. Zniwelowano je cz臋艣ciowo dzi臋ki niedawnemu wprowadzeniu insertRule API.
  • Je艣li dzieje si臋 co艣 nie tak ze styled-components, zawiesza si臋 ca艂a aplikacja.
  • Styled-components nie mo偶na wy艂膮czy膰 do statycznego pliku CSS.
  • Linting ma sporo b艂臋d贸w, np. s膮 problemy z lintowaniem spacji, je偶eli wyst臋puj膮 jakiekolwiek regu艂y, kt贸re specyfikuj膮 ich ilo艣膰 przy u偶ywaniu blok贸w warunkowych styl贸w.
  • 艢cis艂e powi膮zanie z Reactem. Migracja z obecnie u偶ywanego frameworka lub ponowne u偶ycie istniej膮cego komponentu w innym projekcie jest trudne.
  • To nowa technologia bez jasno zdefiniowanej metodyki i strategii.


Moja opinia

Po miesi膮cu korzystania ze styled-components mog臋 stwierdzi膰, 偶e nie jestem jeszcze do ko艅ca przekonany co do ich przewagi nad tworzeniem arkuszy styl贸w, ale zauwa偶am olbrzymi potencja艂. Nawet je艣li u偶ywanie ich w aplikacjach webowych wydaje si臋 troch臋 eksperymentowaniem, 艣wietnie pracowa艂o mi si臋 z nimi w projekcie React Native. Sprawdzaj膮 si臋 o wiele lepiej ni偶 pisanie CSS jako obiekt贸w JavaScript.

My艣l臋, 偶e - pomimo kilku niedoci膮gni臋膰 - styled-components to 艣wietna biblioteka i kolejny krok w ewolucji wsp贸艂czesnego CSS. Wyrazy uznania dla Glena Madderna i Maxa Stoibera - jej tw贸rc贸w.

Chcesz dowiedzie膰 si臋 wi臋cej?聽Do艂膮czam kilka artyku艂贸w i 藕r贸de艂, kt贸rymi wspiera艂em si臋 pisz膮c ten artyku艂 i gor膮co zach臋cam do ich przejrzenia.


Artyku艂 w wersji angielskiej do przeczytania na blogu EL Passion.