Componente cu stil: Biblioteca CSS-in-JS pentru Webul modern

Publicat: 2022-03-11

CSS a fost conceput pentru documente, ceea ce era de așteptat să conțină „vechiul web”. Apariția unor preprocesoare precum Sass sau Less arată că comunitatea are nevoie de mai mult decât oferă CSS. Cu aplicațiile web care devin din ce în ce mai complexe în timp, limitările CSS au devenit din ce în ce mai vizibile și dificil de atenuat.

Componentele stilate valorifică puterea unui limbaj de programare complet — JavaScript — și capacitățile sale de definire a domeniului pentru a ajuta la structurarea codului în componente. Acest lucru ajută la evitarea capcanelor comune ale scrierii și menținerii CSS pentru proiecte mari. Un dezvoltator poate descrie stilul unei componente fără risc de efecte secundare.

Care este problema?

Un avantaj al folosirii CSS este că stilul este complet decuplat de cod. Aceasta înseamnă că dezvoltatorii și designerii pot lucra în paralel fără a interfera unul cu celălalt.

Pe de altă parte, componentele stilate fac mai ușor să cazi în capcana cuplării puternice a stilului și a logicii. Max Stoiber explică cum să evitați acest lucru. Deși ideea de a separa logica și prezentarea nu este cu siguranță nouă, cineva ar putea fi tentat să ia comenzi rapide atunci când dezvolta componente React. De exemplu, este ușor să creați o componentă pentru un buton de validare care se ocupă de acțiunea de clic, precum și de stilul butonului. Este nevoie de ceva mai mult efort pentru a-l împărți în două componente.

Arhitectura Container/Prezentațională

Acesta este un principiu destul de simplu. Componentele fie definesc cum arată lucrurile, fie gestionează datele și logica. Un aspect foarte important al componentelor de prezentare este că acestea nu ar trebui să aibă niciodată dependențe. Ei primesc recuzită și redă DOM (sau copii) în consecință. Containerele, pe de altă parte, știu despre arhitectura datelor (stare, redux, flux etc.), dar nu ar trebui să fie niciodată responsabile pentru afișare. Articolul lui Dan Abramov este o explicație foarte bună și detaliată a acestei arhitecturi.

Amintind SMACSS

Deși Arhitectura scalabilă și modulară pentru CSS este un ghid de stil pentru organizarea CSS, conceptul de bază este unul care este urmat, în cea mai mare parte automat, de componentele stilate. Ideea este de a separa CSS în cinci categorii:

  • Baza conține toate regulile generale.
  • Scopul layout -ului este să definească proprietățile structurale, precum și diverse secțiuni de conținut (antet, subsol, bară laterală, conținut, de exemplu).
  • Modulul conține subcategorii pentru diferitele blocuri logice ale UI.
  • State definește clase de modificatori pentru a indica stările elementelor, de exemplu câmpul în eroare, butonul dezactivat.
  • Tema conține culoare, font și alte aspecte cosmetice care pot fi modificabile sau depind de preferințele utilizatorului.

Păstrarea acestei separări în timp ce utilizați componente cu stil este ușor. Proiectele includ de obicei un fel de normalizare sau resetare CSS. Aceasta se încadrează de obicei în categoria de bază . De asemenea, puteți defini dimensiunea generală a fontului, dimensiunea liniilor etc. Acest lucru se poate face prin CSS normal (sau Sass/Less) sau prin funcția injectGlobal furnizată de styled-components .

Pentru regulile de aspect , dacă utilizați un cadru UI, atunci probabil va defini clase de containere sau un sistem de grilă. Puteți utiliza cu ușurință acele clase împreună cu propriile reguli în componentele de aspect pe care le scrieți.

Modulul este urmat automat de arhitectura componentelor cu stil , deoarece stilurile sunt atașate la componente direct, mai degrabă decât descrise în fișiere externe. Practic, fiecare componentă stilată pe care o scrieți va fi propriul său modul. Vă puteți scrie codul de stil fără să vă faceți griji cu privire la efectele secundare.

State vor fi regulile pe care le definiți în componentele dvs. ca reguli variabile. Pur și simplu definiți o funcție pentru a interpola valorile atributelor dvs. CSS. Dacă utilizați un cadru UI, este posibil să aveți clase utile de adăugat și componentelor dvs. Probabil că veți avea și reguli de pseudo-selector CSS (hover, focus etc.)

Tema poate fi pur și simplu interpolată în componentele dvs. Este o idee bună să definiți tema ca un set de variabile care să fie utilizate în aplicația dvs. Puteți chiar să obțineți culori programatic (folosind o bibliotecă sau manual), de exemplu, pentru a gestiona contrastele și evidențierile. Amintiți-vă că aveți la dispoziție toată puterea unui limbaj de programare!

Aduceți-i împreună pentru o soluție

Este important să le ții împreună, pentru o experiență de navigare mai ușoară; Nu vrem să le organizăm după tip (prezentare vs. logică) ci mai degrabă după funcționalitate.

Astfel, vom avea un folder pentru toate componentele generice (butoane și altele). Celelalte ar trebui organizate în funcție de proiect și de funcționalitățile acestuia. De exemplu, dacă avem funcții de gestionare a utilizatorilor, ar trebui să grupăm toate componentele specifice acelei caracteristici.

Pentru a aplica arhitectura container/prezentare a componentelor stilate unei abordări SMACSS, avem nevoie de un tip suplimentar de componentă: structurală. Ajungem cu trei tipuri de componente; stilizat, structural și container. Deoarece componentele stilate decorează o etichetă (sau componentă), avem nevoie de acest al treilea tip de componentă pentru a structura DOM-ul. În unele cazuri, ar putea fi posibil să se permită unei componente de container să gestioneze structura sub-componentelor, dar atunci când structura DOM devine complexă și este necesară în scopuri vizuale, cel mai bine este să le separați. Un exemplu bun este un tabel, în care DOM-ul devine de obicei destul de verbos.

Exemplu de proiect

Să construim o mică aplicație care afișează rețete pentru a ilustra aceste principii. Putem începe să construim o componentă Rețete. Componenta părinte va fi un controler. Se va ocupa de starea - în acest caz, lista de rețete. De asemenea, va apela o funcție API pentru a prelua datele.

 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} /> ) } }

Va reda lista de rețete, dar nu trebuie (și nu ar trebui) să știe cum. Deci redăm o altă componentă care primește lista de rețete și scoate DOM:

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

Aici, de fapt, vrem să facem o grilă de plăci. Poate fi o idee bună să faceți aspectul actual al plăcilor o componentă generică. Deci, dacă extragem asta, obținem o nouă componentă care arată astfel:

 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; } `

Observați că această componentă este pur de prezentare. Își definește stilul și înfășoară orice copii pe care îi primește într-un alt element DOM stilat care definește cum arată plăcile. Este un bun exemplu despre cum vor arăta din punct de vedere arhitectural componentele dvs. generice de prezentare.

Apoi, trebuie să definim cum arată o rețetă. Avem nevoie de o componentă container pentru a descrie DOM-ul relativ complex și pentru a defini stilul atunci când este necesar. Ajungem cu asta:

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

Observați aici că containerul generează ceva DOM, dar este singura logică pe care o conține. Amintiți-vă că puteți defini stiluri imbricate, deci nu trebuie să creați un element de stil pentru fiecare etichetă care necesită stil. Este ceea ce facem aici pentru numele și cantitatea articolului de ingredient. Bineînțeles, l-am putea împărți și mai mult și să creăm o nouă componentă pentru un ingredient. Depinde de dvs., în funcție de complexitatea proiectului, să determinați granularitatea. În acest caz, este doar o componentă cu stil definită împreună cu restul în fișierul 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 { ... } `

În scopul acestui exercițiu, am folosit ThemeProvider. Injectează tema în recuzita componentelor stilizate. Îl puteți folosi pur și simplu ca color: ${props => props.theme.core_color} , folosesc doar un mic wrapper pentru a proteja de atributele lipsă din temă:

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

De asemenea, puteți defini propriile constante într-un modul și le puteți utiliza în schimb. De exemplu: color: ${styleConstants.core_color}

Pro

Un avantaj al folosirii componentelor stilizate este că le puteți folosi cât de puțin doriți. Puteți folosi cadrul de interfață de utilizare preferat și puteți adăuga componente cu stil deasupra acestuia. Aceasta înseamnă, de asemenea, că puteți migra cu ușurință o componentă de proiect existentă cu o componentă. Puteți alege să stilați cea mai mare parte a aspectului cu CSS standard și să utilizați doar componente cu stil pentru componente reutilizabile.

Contra

Designerii/integratorii de stil vor trebui să învețe JavaScript de bază pentru a gestiona variabilele și a le folosi în locul Sass/Less.

Ei vor trebui, de asemenea, să învețe să navigheze în structura proiectului, deși aș argumenta că găsirea stilurilor pentru o componentă în folderul acelei componente este mai ușoară decât a trebui să găsească fișierul CSS/Sass/Less potrivit care conține regula pe care trebuie să o modificați.

Ei vor trebui, de asemenea, să-și schimbe puțin instrumentele dacă doresc evidențierea sintaxei, listing, etc. Un loc bun pentru a începe este cu acest plugin Atom și acest plugin Babel.