React heeft veel gedaan om de webontwikkeling te vereenvoudigen. De op componenten gebaseerde architectuur van React maakt het in principe eenvoudig om code te ontleden en opnieuw te gebruiken. Het is echter niet altijd duidelijk voor ontwikkelaars hoe ze hun componenten over projecten kunnen delen. In dit bericht laat ik je een paar manieren zien om dit op te lossen.
React heeft het gemakkelijker gemaakt om prachtige, expressieve code te schrijven. Zonder duidelijke patronen voor hergebruik van componenten, wordt de code echter in de loop van de tijd uiteenlopend en wordt deze zeer moeilijk te onderhouden. Ik heb codebases gezien waarbij hetzelfde UI-element tien verschillende implementaties had! Een ander probleem is dat ontwikkelaars meestal de gebruikersinterface en de bedrijfsfunctionaliteit te krap koppelen en later worstelen wanneer de gebruikersinterface verandert.
Vandaag zullen we zien hoe we deelbare UI-componenten kunnen maken en hoe u een consistente ontwerptaal voor uw toepassing kunt creëren.
U hebt een leeg React-project nodig om te beginnen. De snelste manier om dit te doen is via create-react-app, maar het kost wat moeite om Sass hiermee in te stellen. Ik heb een skelet-app gemaakt, die je vanuit GitHub kunt klonen. Je kunt het uiteindelijke project ook vinden in onze tutorial GitHub repo.
Als u wilt uitvoeren, doet u a garen-install
om alle afhankelijkheden op te halen en vervolgens uit te voeren garen start
om de toepassing te openen.
Alle visuele componenten zullen onder de design_system map samen met de overeenkomstige stijlen. Alle globale stijlen of variabelen vallen onder src / stijlen.
Wanneer heb je voor het laatst een look van je ontwerpgenoten gekregen, omdat je de vulling verkeerd hebt gevonden met een halve pixel of als je niet in staat bent om onderscheid te maken tussen verschillende grijstinten? (Er is een verschil tussen #eee
en #efefef
, Er is mij verteld en ik ben van plan het een dezer dagen te ontdekken.)
Een van de doelen van het bouwen van een UI-bibliotheek is het verbeteren van de relatie tussen het ontwerp- en ontwikkelingsteam. Front-endontwikkelaars werken al een tijdje samen met API-ontwerpers en zijn goed in het opzetten van API-contracten. Maar om een of andere reden ontsnapt het ons tijdens de coördinatie met het ontwerpteam. Als je erover nadenkt, zijn er maar een eindig aantal toestanden waarin een UI-element kan bestaan. Als we bijvoorbeeld een headingcomponent moeten ontwerpen, kan het van alles zijn h1
en h6
en kan vetgedrukt, cursief of onderstreept zijn. Het zou eenvoudig moeten zijn om dit te codificeren.
De eerste stap voordat je aan een ontwerpproject begint, is om te begrijpen hoe de roosters zijn gestructureerd. Voor veel apps is het gewoon willekeurig. Dit leidt tot een verstrooid afstandssysteem en maakt het erg moeilijk voor ontwikkelaars om te bepalen welk afstandensysteem te gebruiken. Dus kies een systeem! Ik werd verliefd op het 4px - 8px rastersysteem toen ik er voor het eerst over las. Vasthouden aan dat heeft geholpen bij het vereenvoudigen van veel stylingproblemen.
Laten we beginnen met het opzetten van een basisrooster in de code. We beginnen met een app-component die de lay-out beschrijft.
//src/App.js import Reageren, Component uit 'reageren'; logo importeren uit './logo.svg'; import './App.scss'; importeer Flex, Page, Box, BoxStyle van './design_system/layouts/Layouts'; class-app breidt component render () terug (); standaard app exporteren;Bouw een ontwerpsysteem met React
Een eenvoudige flexbox Midden en dit gaat naar rechts
Vervolgens definiëren we een aantal stijlen en wrapper-componenten.
//design-system/layouts/Layout.js importeren Reageren van 'reageren'; import './layout.scss'; export const BoxBorderStyle = default: 'ds-box-border - default', light: 'ds-box-border - light', thick: 'ds-box-border - thick', export const BoxStyle = standaard: 'ds-box - standaard', doubleSpace: 'ds-box - double-space', noSpace: 'ds-box - no-space' export const Page = (children, fullWidth = true) => const classNames = 'ds-page $ fullWidth? 'ds-page - fullwidth': " '; return (kinderen); ; export const Flex = (children, lastElRight) => const classNames = 'flex $ lastElRight? 'flex-align-right': " '; return (kinderen); ; export const Box = (children, borderStyle = BoxBorderStyle.default, boxStyle = BoxStyle.default, fullWidth = true) => const classNames = 'ds-box $ borderStyle $ boxStyle $ fullWidth? 'ds-box - fullwidth': " '; return (kinderen); ;
Ten slotte zullen we onze CSS-stijlen in SCSS definiëren.
/*design-system/layouts/layout.scss * / @import '... / ... /styles/variables.scss'; $ base-padding: $ base-px * 2; .flex weergave: flex; & .flex-align-right> div: last-child margin-left: auto; .ds-page border: 0px solid # 333; border-left-width: 1px; border-right-width: 1px; &: niet (.ds-page - fullwidth) margin: 0 auto; maximale breedte: 960 px; & .ds-page - fullwidth max-width: 100%; marge: 0 $ base-px * 10; .ds-box border-colour: # f9f9f9; randstijl: stevig; text-align: left; & .ds-box - fullwidth width: 100%; & .ds-box-border - light border: 1px; & .ds-box-border - thick border-width: $ base-px; & .ds-box - standaard padding: $ base-padding; & .ds-box - double-space padding: $ base-padding * 2; & .ds-box - standaard - geen ruimte opvulling: 0;
Er is veel om hier uit te pakken. Laten we beginnen vanaf de bodem. variables.scss is waar we onze globals definiëren zoals kleur en het raster opzetten. Omdat we het 4px-8px-raster gebruiken, zal onze basis 4px zijn. De bovenliggende component is Pagina
, en dit bepaalt de stroom van de pagina. Dan is het element op het laagste niveau een Doos
, die bepaalt hoe inhoud wordt weergegeven op een pagina. Het is gewoon een div
die weet zichzelf contextueel te maken.
Nu hebben we een Houder
component dat meerdere samen lijmen div
s. We hebben gekozen flex-box
, vandaar de creatief genoemde Buigen
bestanddeel.
Het type systeem is een essentieel onderdeel van elke toepassing. Gewoonlijk definiëren we een basis via globale stijlen en overschrijven we waar en wanneer nodig. Dit leidt vaak tot inconsistenties in het ontwerp. Laten we eens kijken hoe dit eenvoudig kan worden opgelost door toe te voegen aan de ontwerpbibliotheek.
Eerst zullen we enkele stijlconstanten en een wrapper-klasse definiëren.
// design-system / type / Type.js import Reageren, Component uit 'reageren'; import './type.scss'; export const TextSize = default: 'ds-text-size - default', sm: 'ds-text-size - sm', lg: 'ds-text-size - lg'; export const TextBold = default: 'ds-text - default', semiibold: 'ds-text - semibold', bold: 'ds-text - bold'; export const Type = (tag = 'span', size = TextSize.default, boldness = TextBold.default, children) => const Tag = '$ tag'; const classNames = 'ds-text $ size $ boldness'; terugkeerkinderen ;
Vervolgens definiëren we de CSS-stijlen die zullen worden gebruikt voor tekstelementen.
/ * ontwerpsysteem / type / type.scss * / @import '... / ... /styles/variables.scss'; $ base-font: $ base-px * 4; .ds-text line-height: 1.8em; & .ds-text-size - standaard font-size: $ base-font; & .ds-text-size - sm font-size: $ base-font - $ base-px; & .ds-text-size - lg font-size: $ base-font + $ base-px; & strong, & .ds-text - semibold font-weight: 600; & .ds-text - bold font-weight: 700;
Dit is een eenvoudig Tekst
component die de verschillende UI-statussen representeert waarin tekst kan staan. We kunnen dit verder uitbreiden om micro-interacties zoals rendering tooltips af te handelen wanneer de tekst wordt bijgesneden, of een andere nugget voor speciale gevallen zoals e-mail, tijd, enz..
Tot dusverre hebben we alleen de meest elementaire elementen gemaakt die in een webtoepassing kunnen bestaan en hebben ze op zich geen zin. Laten we dit voorbeeld uitbreiden door een eenvoudig modaal venster te bouwen.
Eerst definiëren we de componentklasse voor het modale venster.
// design-system / Portal.js import Reageren, Component uit 'reageren'; import ReactDOM van 'react-dom'; import Box, Flex uit './layouts/Layouts'; importeer Type, TextSize, TextAlign uit './type/Type'; import './portal.scss'; exportklasse Portal breidt uit React.Component constructor (props) super (rekwisieten); this.el = document.createElement ('div'); componentDidMount () this.props.root.appendChild (this.el); componentWillUnmount () this.props.root.removeChild (this.el); render () return ReactDOM.createPortal (this.props.children, this.el,); export const Modal = (children, root, closeModal, header) => retourHeader X kinderen
Vervolgens kunnen we de CSS-stijlen voor het modale definiëren.
# modal-root .modal-wrapper background-color: white; grensradius: 10px; max. hoogte: calc (100% - 100 px); maximale breedte: 560 px; breedte: 100%; top: 35%; links: 35%; rechts: auto; onderaan: auto; z-index: 990; positie: absoluut; > div achtergrondkleur: transparant (zwart, .5); positie: absoluut; z-index: 980; boven: 0; rechts: 0; links: 0; onderkant: 0; .close cursor: pointer;
Voor niet-ingewijden, createPortal
lijkt erg op de geven
methode, behalve dat hiermee kinderen worden omgezet in een knooppunt dat buiten de DOM-hiërarchie van de bovenliggende component bestaat. Het werd geïntroduceerd in React 16.
Nu het onderdeel is gedefinieerd, laten we zien hoe we het in een zakelijke context kunnen gebruiken.
//src/App.js import Reageren, Component uit 'reageren'; // ... importeer Type, TextBold, TextSize van './design_system/type/Type'; importeer Modal van './design_system/Portal'; klasse-app breidt component constructor () super () uit; this.state = showModal: false toggleModal () this.setState (showModal:! this.state.showModal); render () // ... this.state.showModal &&Test rendering // ...
We kunnen de modal overal gebruiken en de status in de beller behouden. Eenvoudig, toch? Maar er is een foutje. De knop Sluiten werkt niet. Dat komt omdat we alle componenten hebben gebouwd als een gesloten systeem. Het verbruikt gewoon de rekwisieten die het nodig heeft en negeert de rest. In deze context negeert de tekstcomponent de bij klikken
gebeurtenishandler. Gelukkig is dit een gemakkelijke oplossing.
// In ontwerp-systeem / type / Type.js export const Type = (tag = 'span', size = TextSize.default, boldness = TextBold.default, children, className = ", align = TextAlign.default, ... rest ) => const Tag = '$ tag'; const classNames = 'ds-text $ size $ boldness $ align $ className'; terugkinderen ;
ES6 heeft een handige manier om de resterende parameters als een array uit te pakken. Pas dat toe en verspreid ze naar het onderdeel.
Naarmate uw team schalen, is het moeilijk om iedereen met elkaar te laten synchroniseren over de componenten die beschikbaar zijn. Verhalenboeken zijn een geweldige manier om uw componenten vindbaar te maken. Laten we een basisverhalenboekcomponent opzetten.
Start om te beginnen:
npm i -g @ storybook / cli getstorybook
Hiermee wordt de vereiste configuratie voor het verhalenboek ingesteld. Vanaf hier is het een makkie om de rest van de installatie te doen. Laten we een eenvoudig verhaal toevoegen om verschillende toestanden van te vertegenwoordigen Type
.
import Reageren van 'reageren'; importeer verhalenOf uit '@ storybook / react'; importeer Type, TextSize, TextBold van '... /design_system/type/Type.js'; storiesOf ('Type', module) .add ('standaardtekst', () => (Lorem ipsum )). add ('vetgedrukte tekst', () => (Lorem ipsum )). add ('koptekst', () => (Lorem ipsum ));
Het API-oppervlak is eenvoudig. storiesOf
definieert een nieuw verhaal, meestal uw component. U kunt dan een nieuw hoofdstuk maken met toevoegen
, om de verschillende toestanden van dit onderdeel te laten zien.
Natuurlijk is dit vrij eenvoudig, maar verhalenboeken hebben verschillende add-ons die u zullen helpen functionaliteit aan uw documenten toe te voegen. En heb ik al gezegd dat ze emoji-ondersteuning hebben? 😲
Een nieuw ontwerpsysteem ontwerpen, is heel wat werk en is misschien niet logisch voor een kleinere app. Maar als uw product rijk is en u veel flexibiliteit en controle nodig hebt over wat u bouwt, zal het opzetten van uw eigen UI-bibliotheek u op de langere termijn helpen.
Ik moet nog een goede UI-componentenbibliotheek voor React zien. Mijn ervaring met react-bootstrap en material-ui (de bibliotheek voor React, dat wil zeggen niet het framework zelf) was niet geweldig. In plaats van een volledige UI-bibliotheek te hergebruiken, kan het kiezen van afzonderlijke componenten zinvol zijn. Het implementeren van multi-select is bijvoorbeeld een complex UI-probleem en er zijn talloze scenario's om te overwegen. In dit geval kan het eenvoudiger zijn om een bibliotheek zoals React Select of Select2 te gebruiken.
Een woord van waarschuwing echter. Externe afhankelijkheden, met name UI-plug-ins, vormen een risico. Ze zijn vaak geneigd om hun API's vaak te veranderen of, aan de andere kant, om oude, achterhaalde kenmerken van React te blijven gebruiken. Dit kan van invloed zijn op uw technische bezorging en eventuele wijzigingen kunnen kostbaar zijn. Ik zou adviseren om een wrapper over deze bibliotheken te gebruiken, dus het zal gemakkelijk zijn om de bibliotheek te vervangen zonder meerdere delen van de app aan te raken.
In dit bericht heb ik je enkele manieren getoond om je app te splitsen in atomaire visuele elementen, door ze te gebruiken als Lego-blokken om het gewenste effect te bereiken. Dit vergemakkelijkt het hergebruik en onderhoud van code en maakt het eenvoudig om een consistente gebruikersinterface in uw app te onderhouden.
Deel uw mening over dit artikel in de sectie Opmerkingen!