Styled-Components: Biblioteka CSS-in-JS dla nowoczesnego internetu

Opublikowany: 2022-03-11

CSS został zaprojektowany z myślą o dokumentach, co miało zawierać „stara sieć”. Pojawienie się preprocesorów, takich jak Sass czy Less, pokazuje, że społeczność potrzebuje więcej niż to, co oferuje CSS. Ponieważ aplikacje internetowe stają się z czasem coraz bardziej złożone, ograniczenia CSS stają się coraz bardziej widoczne i trudne do złagodzenia.

Styled-components wykorzystuje moc kompletnego języka programowania — JavaScript — i jego możliwości określania zakresu, aby pomóc uporządkować kod w składniki. Pomaga to uniknąć typowych pułapek związanych z pisaniem i obsługą CSS dla dużych projektów. Deweloper może opisać styl komponentu bez ryzyka wystąpienia efektów ubocznych.

Jaki jest problem?

Jedną z zalet korzystania z CSS jest to, że styl jest całkowicie oddzielony od kodu. Oznacza to, że programiści i projektanci mogą pracować równolegle, nie przeszkadzając sobie nawzajem.

Z drugiej strony styled-components ułatwiają wpadnięcie w pułapkę silnego sprzężenia stylu i logiki. Max Stoiber wyjaśnia, jak tego uniknąć. Chociaż pomysł oddzielenia logiki i prezentacji zdecydowanie nie jest nowy, można pokusić się o pójście na skróty podczas tworzenia komponentów React. Na przykład łatwo jest utworzyć składnik dla przycisku walidacji, który obsługuje akcję kliknięcia, a także styl przycisku. Rozbicie go na dwie części wymaga nieco więcej wysiłku.

Architektura kontenerowa/prezentacyjna

To całkiem prosta zasada. Komponenty albo definiują wygląd rzeczy, albo zarządzają danymi i logiką. Bardzo ważnym aspektem komponentów prezentacji jest to, że nigdy nie powinny mieć żadnych zależności. Otrzymują rekwizyty i odpowiednio renderują DOM (lub dzieci). Z drugiej strony kontenery znają architekturę danych (stan, redux, strumień itp.), ale nigdy nie powinny być odpowiedzialne za wyświetlanie. Artykuł Dana Abramova jest bardzo dobrym i szczegółowym wyjaśnieniem tej architektury.

Zapamiętywanie SMACSS

Chociaż Scalable and Modular Architecture for CSS jest przewodnikiem po stylach organizowania CSS, podstawową koncepcją jest taka, za którą, w większości automatycznie, podążają styled-components. Chodzi o to, aby podzielić CSS na pięć kategorii:

  • Baza zawiera wszystkie ogólne zasady.
  • Zadaniem layoutu jest zdefiniowanie właściwości strukturalnych, a także różnych sekcji treści (na przykład nagłówek, stopka, pasek boczny, treść).
  • Moduł zawiera podkategorie dla różnych bloków logicznych interfejsu użytkownika.
  • Stan definiuje klasy modyfikatorów wskazujące stany elementów, np. pole z błędem, przycisk nieaktywny.
  • Motyw zawiera kolor, czcionkę i inne aspekty kosmetyczne, które mogą być modyfikowane lub zależne od preferencji użytkownika.

Zachowanie tej separacji podczas używania styled-components jest łatwe. Projekty zazwyczaj zawierają jakiś rodzaj normalizacji lub resetowania CSS. Zwykle należy to do kategorii Base . Możesz także zdefiniować ogólny rozmiar czcionki, rozmiar linii itp. Można to zrobić za pomocą normalnego CSS (lub Sass/Less) lub za pomocą funkcji injectGlobal dostarczanej przez styled-components .

W przypadku reguł układu , jeśli używasz frameworka interfejsu użytkownika, prawdopodobnie zdefiniuje on klasy kontenerów lub system siatki. Możesz łatwo używać tych klas w połączeniu z własnymi regułami w tworzonych komponentach układu.

Za modułem automatycznie podąża architektura styled-components , ponieważ style są dołączane bezpośrednio do komponentów, a nie opisane w plikach zewnętrznych. Zasadniczo każdy stylizowany komponent, który napiszesz, będzie osobnym modułem. Możesz napisać swój kod stylizacji, nie martwiąc się o skutki uboczne.

Stan będzie regułami, które zdefiniujesz w swoich komponentach jako reguły zmienne. Po prostu definiujesz funkcję do interpolacji wartości atrybutów CSS. Jeśli używasz frameworka UI, możesz mieć przydatne klasy, które możesz dodać do swoich komponentów. Prawdopodobnie będziesz mieć również reguły pseudoselektora CSS (najechanie, fokus itp.)

Motyw można po prostu interpolować w swoich komponentach. Dobrym pomysłem jest zdefiniowanie motywu jako zestawu zmiennych, które będą używane w całej aplikacji. Możesz nawet programowo (przy użyciu biblioteki lub ręcznie) wyprowadzać kolory, na przykład w celu obsługi kontrastów i podświetleń. Pamiętaj, że masz do dyspozycji pełną moc języka programowania!

Połącz je, aby znaleźć rozwiązanie

Ważne jest, aby trzymać je razem, aby ułatwić nawigację; Nie chcemy organizować ich według typu (prezentacja vs logika), ale raczej według funkcjonalności.

W ten sposób będziemy mieli folder dla wszystkich ogólnych komponentów (przycisków itp.). Pozostałe powinny być zorganizowane w zależności od projektu i jego funkcjonalności. Na przykład, jeśli mamy funkcje zarządzania użytkownikami, powinniśmy pogrupować wszystkie komponenty specyficzne dla tej funkcji.

Aby zastosować architekturę kontenera/prezentacji styled-components do podejścia SMACSS, potrzebujemy dodatkowego typu komponentu: strukturalnego. Kończymy z trzema rodzajami komponentów; stylizowane, strukturalne i kontenerowe. Ponieważ styled-components dekorują tag (lub komponent), potrzebujemy tego trzeciego typu komponentu do strukturyzowania DOM. W niektórych przypadkach może być możliwe zezwolenie komponentowi kontenera na obsługę struktury podkomponentów, ale gdy struktura DOM staje się złożona i jest wymagana do celów wizualnych, najlepiej je rozdzielić. Dobrym przykładem jest tabela, w której DOM zazwyczaj jest dość szczegółowy.

Przykładowy projekt

Zbudujmy małą aplikację wyświetlającą przepisy ilustrujące te zasady. Możemy zacząć budować komponent Recipes. Komponent nadrzędny będzie kontrolerem. Zajmie się stanem — w tym przypadku listą przepisów. Wywoła również funkcję API w celu pobrania danych.

 class Recipes extends Component{ constructor (props) { super(props); this.state = { recipes: [] }; } componentDidMount () { this.loadData() } loadData () { getRecipes().then(recipes => { this.setState({recipes}) }) } render() { let {recipes} = this.state return ( <RecipesContainer recipes={recipes} /> ) } }

Wyświetli listę przepisów, ale nie musi (i nie powinien) wiedzieć, jak to zrobić. Więc renderujemy kolejny komponent, który pobiera listę receptur i wyników DOM:

 class RecipesContainer extends Component{ render() { let {recipes} = this.props return ( <TilesContainer> {recipes.map(recipe => (<Recipe key={recipe.id} {...recipe}/>))} </TilesContainer> ) } }

Tutaj właściwie chcemy stworzyć siatkę płytek. Dobrym pomysłem może być uczynienie rzeczywistego układu kafelków elementem ogólnym. Jeśli więc to wyodrębnimy, otrzymamy nowy komponent, który wygląda tak:

 class TilesContainer extends Component { render () { let {children} = this.props return ( <Tiles> { React.Children.map(children, (child, i) => ( <Tile key={i}> {child} </Tile> )) } </Tiles> ) } }

TilesStyles.js:

 export const Tiles = styled.div` padding: 20px 10px; display: flex; flex-direction: row; flex-wrap: wrap; ` export const Tile = styled.div` flex: 1 1 auto; ... display: flex; & > div { flex: 1 0 auto; } `

Zauważ, że ten składnik ma charakter czysto prezentacyjny. Definiuje swój styl i otacza wszystkie dzieci, które otrzymuje, wewnątrz innego stylizowanego elementu DOM, który definiuje wygląd kafelków. Jest to dobry przykład tego, jak będą wyglądać Twoje ogólne komponenty prezentacyjne pod względem architektonicznym.

Następnie musimy określić, jak wygląda przepis. Potrzebujemy komponentu kontenera, aby opisać stosunkowo złożony DOM, a także w razie potrzeby zdefiniować styl. Kończymy na tym:

 class RecipeContainer extends Component { onChangeServings (e) { let {changeServings} = this.props changeServings(e.target.value) } render () { let {title, ingredients, instructions, time, servings} = this.props return ( <Recipe> <Title>{title}</Title> <div>{time}</div> <div>Serving <input type="number" min="1" max="1000" value={servings} onChange={this.onChangeServings.bind(this)}/> </div> <Ingredients> {ingredients.map((ingredient, i) => ( <Ingredient key={i} servings={servings}> <span className="name">{ingredient.name}</span> <span className="quantity">{ingredient.quantity * servings} {ingredient.unit}</span> </Ingredient> ))} </Ingredients> <div> {instructions.map((instruction, i) => (<p key={i}>{instruction}</p>))} </div> </Recipe> ) } }

Zauważ, że kontener wykonuje pewne generowanie DOM, ale jest to jedyna logika, którą zawiera. Pamiętaj, że możesz zdefiniować style zagnieżdżone, więc nie musisz tworzyć elementu stylizowanego dla każdego tagu, który wymaga stylizacji. Robimy to tutaj dla nazwy i ilości składnika. Oczywiście moglibyśmy to dalej podzielić i stworzyć nowy składnik dla składnika. To zależy od Ciebie — w zależności od złożoności projektu — aby określić szczegółowość. W tym przypadku jest to po prostu stylizowany komponent zdefiniowany wraz z resztą w pliku RecipeStyles:

 export const Recipe = styled.div` background-color: ${theme('colors.background-highlight')}; `; export const Title = styled.div` font-weight: bold; ` export const Ingredients = styled.ul` margin: 5px 0; ` export const Ingredient = styled.li` & .name { ... } & .quantity { ... } `

Na potrzeby tego ćwiczenia użyłem ThemeProvider. Wstrzykuje motyw w rekwizyty stylizowanych komponentów. Możesz po prostu użyć go jako color: ${props => props.theme.core_color} , używam tylko małego opakowania, aby chronić przed brakującymi atrybutami w motywie:

 const theme = (key) => (prop) => _.get(prop.theme, key) || console.warn('missing key', key)

Możesz także zdefiniować własne stałe w module i użyć ich zamiast tego. Na przykład: color: ${styleConstants.core_color}

Plusy

Zaletą używania styled-components jest to, że możesz ich używać tak mało, jak chcesz. Możesz użyć swojego ulubionego frameworka UI i dodać do niego styled-components . Oznacza to również, że możesz łatwo migrować istniejący komponent projektu po komponencie. Możesz wybrać stylizację większości układu za pomocą standardowego CSS i używać tylko styled-components dla komponentów wielokrotnego użytku.

Cons

Projektanci/integratorzy stylów będą musieli nauczyć się bardzo podstawowego JavaScript, aby obsługiwać zmienne i używać ich zamiast Sass/Less.

Będą musieli również nauczyć się poruszać w strukturze projektu, chociaż twierdzę, że znalezienie stylów dla komponentu w folderze tego komponentu jest łatwiejsze niż znalezienie odpowiedniego pliku CSS/Sass/Less, który zawiera regułę, którą musisz zmodyfikować.

Będą również musieli trochę zmienić swoje narzędzia, jeśli chcą podświetlać składnię, linting itp. Dobrym miejscem na rozpoczęcie jest ta wtyczka Atom i ta wtyczka Babel.