I dagens verden av Javascript Application rammer, er designfilosofien den viktigste differensieringsfaktoren. Hvis du sammenligner de populære JS-rammene, som EmberJS, AngularJS, Backbone, Knockout, etc., er du sikker på å finne forskjeller i deres abstraksjoner, tankemodeller og selvfølgelig terminologien. Dette er en direkte konsekvens av den underliggende designfilosofien. Men i prinsippet gjør de alle en ting, som er å abstrahere DOM på en slik måte at du ikke håndterer direkte med HTML-elementer.
Jeg tror personlig at et rammeverk blir interessant når det gir et sett av abstraksjoner som muliggjør en annen tenkemåte. I dette aspektet, reagerer, vil det nye JS-rammeverket fra folkene på Facebook, tvinge deg til å tenke på (i noen grad) hvordan du dekomponerer brukergrensesnittet og samspillet mellom søknaden din. Etter å ha nådd versjon 0.4.1 (som av denne skrivingen) gir React en overraskende enkel, men likevel effektiv modell for å bygge JS apps som blander en herlig cocktail av en annen type.
I denne artikkelen vil vi utforske byggeklossene i React og omfavne en stil av tenkning som kan virke motstridende på første gang. Men som React docs sier: "Gi det fem minutter" og så vil du se hvordan denne tilnærmingen blir mer naturlig.
Historien om React startet innen Facebook, hvor den brygget for en stund. Etter å ha nådd en stabilt nok stat, bestemte utviklerne seg for å åpne den for noen måneder tilbake. Interessant er Instagram-nettsiden også drevet av React Framework.
React nærmer seg DOM-abstraksjonsproblemet med en litt annen oppgave. For å forstå hvordan dette er annerledes, lar vi raskt glosse over teknikkene vedtatt av rammene jeg nevnte tidligere.
MVC-modellen (Model-View-Controller) er fundamentalt for UI-utvikling, ikke bare i webapplikasjoner, men i front-end-applikasjoner på alle plattformer. I tilfelle av webapps er DOM den fysiske representasjonen av en visning. DOM-en er generert fra en tekstlig HTML-mal som trekkes fra en annen fil, en script-blokk eller en forhåndskompilert malfunksjon. De Utsikt
er en enhet som bringer tekstmalen til liv som et DOM-fragment. Det setter også opp hendelseshåndterer og tar seg av å manipulere DOM-treet som en del av sin livssyklus.
For Utsikt
For å være nyttig, trenger den å vise noen data, og muligens tillate brukerinteraksjon. Dataene er Modell
, som kommer fra en datakilde (en database, web-tjeneste, lokal lagring, etc.). Rammer gir en måte å "binde" dataene til visningen, slik at endringer i data reflekteres automatisk med endringer i visningen. Denne automatiske prosessen kalles data-bindende og det er APIer / teknikker for å gjøre dette så sømløst som mulig.
MVC triaden er fullført av Controller
, som engasjerer Utsikt
og Modell
og orkestrerer dataflyten (Modell
) inn i det Utsikt
og bruker-hendelser ut fra Utsikt
, muligens fører til endringer i Modell
.
Rammer som automatisk håndterer datastrømmen frem og tilbake mellom Vis og modell, opprettholder en intern event-loop. Denne hendelsesløkken er nødvendig for å lytte til bestemte brukerhendelser, dataendringshendelser, eksterne utløsere, osv., Og deretter avgjøre om det er noen endring fra forrige løp i løkken. Hvis det er endringer, i hver ende (Vis eller Modell), sikrer rammen at begge blir brakt tilbake i synkronisering.
Med React tar Vis-delen av MVC-triaden en fremtredende rolle og rulles inn i en enhet kalt Komponent
. Komponenten opprettholder en ubrukt eiendomspose som kalles Rekvisitter
, og a stat
som representerer brukerens drevne tilstand av brukergrensesnittet. Visningsgenereringsdelen av Komponent
er ganske interessant og muligens årsaken til at React skiller seg ut i forhold til andre rammer. I stedet for å bygge en fysisk DOM direkte fra en malfil / script / funksjon, vil Komponent
genererer et mellomliggende DOM som er en stand-in for den virkelige HTML DOM. Et ekstra trinn blir da tatt for å oversette denne mellomliggende DOM til den virkelige HTML DOM.
Som en del av den mellomliggende DOM generasjonen, er Komponent
Henger også hendelsesbehandlere og binder dataene i Rekvisitter
og stat
.
Hvis ideen om en mellomliggende DOM høres litt fremmed, må du ikke være for alarmert. Du har allerede sett denne strategien vedtatt av språkkjøretider (aka virtuelle maskiner) for tolkede språk. Vår egen JavaScript runtime, genererer først en mellomliggende representasjon før du spytter ut den opprinnelige koden. Dette gjelder også for andre VM-baserte språk som Java, C #, Ruby, Python, osv.
React vedtar smart denne strategien for å lage en mellomliggende DOM før du genererer den endelige HTML DOM. Mellom-DOM er bare en JavaScript-objektgraf og blir ikke gjengitt direkte. Det er et oversettelsestrinn som skaper den virkelige DOM. Dette er den underliggende teknikken som gjør React gjøre DOM-manipulasjoner raskt.
For å få et bedre bilde av hvordan React gjør det hele, la oss dykke litt dypere; starter med Komponent
. Komponenten er den primære byggeblokken i React. Du kan komponere brukergrensesnittet til søknaden din ved å sette sammen et tre av komponenter. Hver komponent gir en implementering for render ()
metode, der den lager mellomliggende DOM. ringe React.renderComponent ()
på roten Komponent resulterer i rekursivt å gå ned i komponent-treet og bygge opp mellom-DOM. Mellom-DOM konverteres da til den ekte HTML DOM.
Siden mellomliggende DOM-opprettelsen er en integrert del av komponenten, gir React en praktisk XML-basert utvidelse til JavaScript, kalt JSX, for å bygge komponent-treet som et sett med XML-noder. Dette gjør det enklere å visualisere og begrunnes om DOM. JSX forenkler også foreningen av hendelsesbehandlere og egenskaper som xml-attributter. Siden JSX er et utvidelsesspråk, er det et verktøy (kommandolinje og i nettleser) for å generere den endelige JavaScript. En JSX XML-kode kaster direkte til en komponent. Det er verdt å påpeke at React fungerer uavhengig av JSX og JSX-språket gjør det enkelt å lage mellomliggende DOM.
Kjerne React Framework kan lastes ned fra deres hjemmeside. I tillegg til JSX → JS-transformasjonen kan du enten bruke JSXTransformer i nettleseren eller bruke kommandolinjeverktøyet, kalt reagensverktøy (installert via NPM). Du trenger en installasjon av Node.js for å laste den ned. Kommandolinjeverktøyet lar deg forkompilere JSX-filene og unngå oversettelsen i nettleseren. Dette anbefales definitivt hvis JSX-filene dine er store eller mange i nummer.
OK, vi har sett mye teori så langt, og jeg er sikker på at du er kløe for å se noen ekte kode. La oss dykke inn i vårt første eksempel:
/ ** @jsx React.DOM * / var Simple = React.createClass (getInitialState: function () return count: 0;, handleMouseDown: function () alert ('Jeg ble fortalt:' + dette. props.message); this.setState (count: this.state.count + 1);, gjengi: funksjon () retur; ); React.renderComponent (Gi meg beskjeden!Melding overført This.state.count tid (s), document.body);
Selv om det er enkle, dekker koden ovenfor en god mengde React overflaten:
React.createClass
og passerer inn i en gjenstand som utfører noen kjernefunksjoner. Den viktigste er den render ()
, som genererer mellomliggende DOM.
syntaks er nyttig for å inkorporere JavaScript-uttrykk for attributter (onMouseDown = this.handleClick
) og barneknuter ( This.state.count
). Hendelseshandlere som er assosiert med -syntaxen, bindes automatisk til forekomsten av komponenten. Og dermed dette
inne i hendelseshåndteringsfunksjonen refererer til komponenteksemplet. Kommentaren på første linje / ** @jsx React.DOM * /
er en kø for JSX-transformatoren for å gjøre oversettelsen til JS. Uten denne kommentarlinjen, vil ingen oversettelse finne sted. Vi kan kjøre kommandolinjeværktøyet (jsx) i watch-modus og automatisk kompilere endringer fra JSX → JS. Kildefilene er inne / src mappe og utgangen genereres i /bygge.
jsx - watch src / build /
Her er den genererte JS-filen:
/ ** @jsx React.DOM * / var Simple = React.createClass (displayName: 'Simple', getInitialState: funksjon () return count: 0;, handleMouseDown: function () alert fortalte: '+ this.props.message); this.setState (count: this.state.count + 1);, gjengi: funksjon () return React.DOM.div (null, React.DOM.div (className: "clicker", onMouseDown: this.handleMouseDown, "Gi meg meldingen!"), React.DOM.div (className: "message", "Message conveyed", React.DOM.span className: "count", this.state.count), "time (s)"));); React.renderComponent (Simple (message: "Keep it Simple"), document.body);
Legg merke til hvordan og
tagger kart til forekomster av
React.DOM.div
og React.DOM.span
.
handleMouseDown
, vi benytter oss av this.props
å lese budskap eiendom som ble sendt inn. Vi satte budskap på siste linje i koden, i anropet til React.renderComponent ()
hvor vi lager
komponent. Meningen med this.props
er å lagre dataene som ble sendt inn i komponenten. Det regnes som uendelig, og bare en komponent på høyere nivå får lov til å gjøre endringer og sende det ned til komponenttreet.handleMouseDown
Vi stiller også noen brukerstatus med this.setState ()
for å spore hvor mange ganger meldingen ble vist. Du vil legge merke til at vi bruker this.state
i render ()
metode. Når som helst du ringer setState ()
, Reakt utløser også render ()
metode for å holde DOM synkronisert. I tillegg React.renderComponent ()
, setState ()
er en annen måte å tvinge en visuell oppdatering på.Hendelsene som er eksponert på mellomliggende DOM, for eksempel onMouseDown
, fungere også som et lag av indirection før de blir satt på real-DOM. Disse hendelsene er derfor referert til som Syntetiske hendelser. React vedtar hendelsesdelegasjon, som er en velkjent teknikk, og vedlegger hendelser bare på rotnivået til real-DOM. Dermed er det bare en sann event-handler på real-DOM. I tillegg gir disse syntetiske hendelsene også et konsistensnivå ved å skjule nettleser- og elementforskjeller.
Kombinasjonen av mellomliggende DOM og syntetiske hendelser gir deg en standard og konsistent måte å definere brukergrensesnitt på forskjellige nettlesere og til og med enheter.
Komponenter i React-rammeverket har en spesifikk livssyklus og legemliggjør en statsmaskin som har tre forskjellige tilstander.
Komponenten kommer til liv etter å ha vært montert. Montering resulterer i å gå gjennom et gjengivelsespass som genererer komponent-treet (mellomliggende DOM). Dette treet er konvertert og plassert i en container-node av den virkelige DOM. Dette er et direkte utfall av anropet til React.renderComponent ()
.
Når den er montert, forblir komponenten i Oppdater stat. En komponent blir oppdatert når du endrer tilstand ved hjelp av setState ()
eller endre rekvisitter med setProps ()
. Dette resulterer igjen i å ringe render ()
, som bringer DOM synkronisert med dataene (Rekvisitter
+ stat
). Mellom påfølgende oppdateringer beregner React deltaet mellom det forrige komponenttreet og det nylig genererte treet. Dette er et svært optimalisert trinn (og et flaggskip-funksjon) som minimerer manipulasjonen på den virkelige DOM.
Den endelige staten er umontert. Dette skjer når du kaller eksplisitt React.unmountAndReleaseReactRootNode ()
eller automatisk hvis en komponent var et barn som ikke lenger var generert i en render ()
anrop. Oftest trenger du ikke å håndtere dette og bare la React gjøre det riktige.
Nå ville det vært et stort referanse, hvis React ikke fortalte deg når det flyttet mellom Montert-Update-Umontert stater. Heldigvis er det ikke tilfelle, og det er kroker du kan overstyre for å bli varslet om endringer i livssyklusen. Navnene snakker for seg selv:
getInitialState ()
: Forbered opprinnelig tilstand for komponentencomponentWillMount ()
componentDidMount ()
componentWillReceiveProps ()
shouldComponentUpdate ()
: nyttig hvis du vil kontrollere når en gjengivelse skal hoppes over. componentWillUpdate ()
render ()
componentDidUpdate ()
componentWillUnmount ()
De componentWill *
metoder kalles før staten endres og componentDid *
metoder kalles etter.
Innenfor et komponent-tre skal data alltid strømme ned. En overordnet komponent skal angi Rekvisitter
av en barnekomponent for å overføre data fra foreldrene til barnet. Dette kalles som Eier-Owned par. På den annen side vil brukerhendelser (mus, tastatur, berøringer) alltid boble opp fra barnet helt til rotkomponenten, med mindre det håndteres mellom.
Når du oppretter mellomliggende DOM i render ()
, du kan også tilordne en ref
eiendom til en barnekomponent. Du kan da referere til det fra foreldrene ved hjelp av refs
eiendom. Dette er avbildet i utsnittet nedenfor.
gjengivelse: funksjon () // Sett en ref returThis.state.count; handleMouseDown: funksjon () // Bruk ref console.log (this.refs.counter.innerHTML); ,
Som en del av komponentmetadataene kan du angi starttilstanden (getInitialState ()
), som vi så tidligere innen livscyklusmetodene. Du kan også angi standardverdiene til rekvisita med getDefaultProps ()
og også etablere noen valideringsregler på disse rekvisitaene ved hjelp av propTypes
. Dokumentene gir en fin oversikt over de ulike typer valideringer (type sjekker, påkrevd, etc.) du kan utføre.
React støtter også konseptet a mixin å trekke ut gjenbrukbare stykker av atferd som kan injiseres i forskjellige komponenter. Du kan passere mixins ved hjelp av mixins
egenskap av en komponent.
Nå kan vi bli virkelige og bygge en mer omfattende komponent som bruker disse funksjonene.
I dette eksemplet vil vi bygge en redaktør som aksepterer et enkelt DSL (Domain Specific Language) for å lage former. Når du skriver inn, ser du tilsvarende utdata på siden, og gir deg tilbakemelding på nettet.
DSL lar deg lage tre typer former: Ellipse, rektangel og tekst. Hver form er spesifisert på en egen linje sammen med en haug med stylingsegenskaper. Syntaxen er grei og låner litt fra CSS. For å analysere en linje, bruker vi en Regex som ser ut som:
var formRegex = / (rect | ellipse | tekst) (\ s [a-z] +: \ s [a-z0-9] +;) * / i;
Som et eksempel beskriver følgende sett med linjer to rektangler og en tekstetikett ...
// Reag etiketttekst verdi: React; farge: # 00D8FF; skriftstørrelse: 48px; tekstskygge: 1px 1px 3px # 555; polstring: 10px; venstre: 100px; topp: 100px; // venstre logo rekt bakgrunn: url (react.png) no-repeat; grense: ingen; bredde: 38; høyde: 38; venstre: 60px; topp: 120px; // høyre logo rett bakgrunn: url (react.png) no-repeat; grense: ingen; bredde: 38; høyde: 38; venstre: 250px; topp: 120px;
... genererer produksjonen vist nedenfor:
OK, la oss fortsette å bygge denne redaktøren. Vi starter med HTML-filen (index.html
), der vi legger inn toppnivåoppslaget og inkluderer bibliotekene og programskriptene. Jeg viser bare de relevante delene her:
I den ovennevnte koden, er container
div har vår React generated DOM. Våre søknadsskript er inkludert fra /bygge
katalogen. Vi bruker JSX i våre komponenter og kommandolinjevaktaren (jsx
), setter de konverterte JS-filene inn i /bygge
. Merk at denne overvåkerkommandoen er en del av reagere-verktøy
NPM-modul.
jsx - watch src / build /
Redaktøren er delt inn i et sett med komponenter, som er oppført nedenfor:
mixins
eiendom.Forholdet mellom disse enhetene vises i det annoterte komponent-treet:
Leter på implementeringen av noen av disse komponentene, begynner med ShapeEditor.
/ ** @jsx React.DOM * / var ShapeEditor = React.createClass (componentWillMount: funksjon () this._parser = ny ShapeParser ();, getInitialState: funksjon () return text: ";, gjengi: funksjon () var former = this._parser.parse (this.state.text); var tree = (); tilbake treet; , handleTextChange: funksjon (hendelse) this.setState (text: event.target.value));
Som navnet antyder, gir ShapeEditor redigeringsopplevelsen ved å generere og live tilbakemelding på
onChange
hendelse (hendelser i React er alltid kalt med kamel tilfelle) på og på hver endring setter du
tekst
Egenskapen til komponentens stat
. Som nevnt tidligere, når du angir at staten bruker setState ()
, gjengivelse kalles automatisk. I dette tilfellet er det render ()
av ShapeEditor blir kalt hvor vi analyserer teksten fra staten og gjenoppbygger figurene. Vær oppmerksom på at vi starter med en innledende tilstand med tom tekst, som er angitt i getInitialState ()
krok.
For å analysere teksten i et sett med former, bruker vi en forekomst av ShapeParser
. Jeg har slått ut detaljene fra parseren for å holde diskusjonen fokusert på React. Parser-forekomsten er opprettet i componentWillMount ()
kroken. Dette kalles like før komponentfestene og er et godt sted å gjøre noen initialiseringer før den første gjengivelsen skjer.
Det anbefales generelt at du trakterer hele din komplekse behandling gjennom render ()
metode. Hendelsehandlere har bare satt staten mens render ()
er knutepunktet for all din kjernelogikk.
De ShapeEditor
bruker denne ideen til å analysere innsiden av dens render ()
og videresender de oppdagede formene ved å sette inn figurer
eiendom av ShapeCanvas
. Slik går data ned i komponenttreet, fra eieren (ShapeEditor
) til den eide (ShapeCanvas
).
En siste ting å merke seg her er at vi har den første linjeproblemen som viser JSX → JS-oversettelse.
Deretter går vi videre til ShapeCanvas og Ellipse, rektangel og tekstkomponenter.
p> ShapeCanvas
er ganske grei med sitt sentrale ansvar for å generere de respektive
,
og
komponenter fra de bestått i formdefinisjoner (this.props.shapes
). For hver form passerer vi i de analyserte egenskapene med attributtuttrykket: egenskaper = shape.properties
.
/ ** @jsx React.DOM * / var ShapeCanvas = React.createClass (getDefaultProps: funksjon () retur former: [];, gjengi: funksjon () var selv = dette; var shapeTree =this.props.shapes.map (funksjon (er) return self._createShape (s);); var noTree =Ingen figurer funnet; returner this.props.shapes.length> 0? shapeTree: noTree; , _createShape: funksjon (form) return this._shapeMap [shape.type] (form); , _shapeMap: ellipse: funksjon (form) return; , rekt: funksjon (form) return ; , tekst: funksjon (form) return ; );
En ting forskjellig her er at vårt komponent-tre er ikke statisk, som vi har i ShapeEditor. I stedet er det dynamisk generert ved looping over passet i former. Vi viser også "Ingen former funnet"
melding hvis det ikke er noe å vise.
Alle former har en lignende struktur og er bare forskjellig i stylingen. De benytter seg også av ShapePropertyMixin
å håndtere stilgenerasjonen.
Her er Ellipse:
/ ** @jsx React.DOM * / var Ellipse = React.createClass (mixins: [ShapePropertyMixin], gjengi: funksjon () var style = this.extractStyle (true); style ['border-radius'] = ' 50% 50% '; returnere ; );
Gjennomførelsen for extractStyle ()
er levert av ShapePropertyMixin
.
Rektangulære komponenten følger med, selvfølgelig uten border-radius stil. Tekstkomponenten har en ekstra eiendom som kalles verdi
som setter den indre teksten for .
Her er Tekst, for å gjøre dette klart:
/ ** @jsx React.DOM * / var Text = React.createClass (mixins: [ShapePropertyMixin], gjengi: funksjon () var style = this.extractStyle (false); returnThis.props.properties.value; );
app.js
er hvor vi tar alt sammen. Her lager vi rotkomponenten, den ShapeEditor
og gir også støtte for å bytte mellom noen få eksempler. Når du velger en annen prøve fra rullegardinmenyen, laster vi inn noen forhåndsdefinert tekst i ShapeEditor
og forårsake ShapeCanvas
å oppdatere. Dette skjer i readShapes ()
metode.
/ ** @jsx React.DOM * / var shapeEditor =; React.renderComponent (shapeEditor, document.getElementsByClassName ('container') [0]); val (), tekst = SHAPES [fil] || "; $ ('. editor') .val (tekst); shapeEditor.setState ( tekst: tekst); / / tvinge en gjengivelse $ ('.former-plukker'). endre (readShapes); readShapes (); // load time
Å utøve den kreative siden, her er en robot bygget ved hjelp av Shape Editor:
Puh! Dette har vært en ganske lang artikkel, og etter å ha nådd dette punktet, bør du ha en følelse av prestasjon!
Vi har utforsket mange konsepter her: Komponentenes integrerte rolle i rammen, bruk av JSX for å enkelt beskrive et komponent tre (aka intermediate DOM), forskjellige kroker for å plugge inn i komponentens livscykel, bruk av stat
og Rekvisitter
å kjøre gjengivelsesprosessen, bruk Mixins til å regne ut gjenbrukbar oppførsel og til slutt trekke alt dette sammen med Shape Editor-eksempelet.
Jeg håper denne artikkelen gir deg nok boost til å bygge noen React apps for deg selv. For å fortsette leting, er det noen få nyttige lenker: