Leveling op med React: React Router

denne tutorial er den første af en tredelt serie på React af Brad Vestfald. Da Brad slog mig dette, han påpegede, at der er en god mængde tutorials om at komme i gang i React, men ikke så meget om, hvor man skal hen derfra. Hvis du er helt ny til at reagere, anbefaler jeg at se denne introvideo. Denne serie fortsætter, hvor det grundlæggende forlader.

artikelserie:

  1. React Router (du er her!)
  2. Beholderkomponenter

Advarsel! Denne artikel blev skrevet pre-React Router 4, som er blevet et mere standard valg til routing i React. Der er en ny artikel, der dækker React Router 4 her bør du helt sikkert læse.

da jeg først lærte, fandt jeg masser af begynderguider (dvs.1, 2, 3, 4), der viste, hvordan man laver enkeltkomponenter og gengiver dem til DOM. De gjorde et godt stykke arbejde med at undervise i det grundlæggende som JS og rekvisitter, men jeg kæmpede med at finde ud af, hvordan React fungerer i det større billede – som en ægte Single Page Application (SPA). Da denne serie dækker meget materiale, dækker den ikke de absolutte begynderbegreber. I stedet starter det med antagelsen om, at du allerede forstår, hvordan du opretter og gengiver mindst en komponent.

For hvad det er værd, her er nogle andre gode guider, der sigter mod begyndere:

  • reagere.js og hvordan passer det ind i alt andet?
  • Rethinking (Industri) Bedste Praksis
  • React.JS Introduktion til folk, der kender lige nok til at komme forbi

seriekode

denne serie leveres også med noget kode at lege med på GitHub. Gennem hele serien bygger vi en grundlæggende SPA med fokus på brugere og kontroller.

for at holde tingene enkle og korte starter eksemplerne i denne serie med at antage, at React and React Router hentes fra en CDN. Så du vil ikke se require() eller import i de umiddelbare eksempler nedenfor. I slutningen af denne tutorial introducerer vi dog Babel og Babel til GitHub guides. På det tidspunkt er det hele ES6!

React-Router

React er ikke en ramme, det er et bibliotek. Derfor løser det ikke alle en applikations behov. Det gør et godt stykke arbejde med at skabe komponenter og levere et system til styring af staten, men at skabe et mere komplekst SPA kræver en understøttende rollebesætning. Den første, vi vil se på, er React Router.

hvis du har brugt en front-end router før, vil mange af disse begreber være bekendt. Men i modsætning til enhver anden router, jeg har brugt før, bruger React Router JS, som måske ser lidt underligt ud i starten.

som primer er det sådan, det er at gengive en enkelt komponent:

var Home = React.createClass({ render: function() { return (<h1>Welcome to the Home Page</h1>); }});ReactDOM.render(( <Home />), document.getElementById('root'));

her er hvordan Home komponenten ville blive gengivet med React Router:

...ReactDOM.render(( <Router> <Route path="/" component={Home} /> </Router>), document.getElementById('root'));

bemærk, at <Router> og <Route> er to forskellige ting. De er teknisk reaktive komponenter, men de skaber faktisk ikke DOM selv. Selvom det kan se ud som om <Router> selv bliver gengivet til 'root', definerer vi faktisk bare regler om, hvordan vores applikation fungerer. Bevæger sig fremad, vil du se dette koncept ofte: komponenter eksisterer undertiden ikke for at skabe DOM selv, men for at koordinere andre komponenter, der gør det.

i eksemplet definerer <Route> en regel, hvor besøg på hjemmesiden / vil gøre Homekomponenten til 'root'.

flere ruter

i det foregående eksempel er den enkelte rute meget enkel. Det giver os ikke meget værdi, da vi allerede havde evnen til at gengive Home – komponenten uden at routeren var involveret.

React Routers strøm kommer ind, når vi bruger flere ruter til at definere, hvilken komponent der skal gengives, baseret på hvilken sti der i øjeblikket er aktiv:

ReactDOM.render(( <Router> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> <Route path="/widgets" component={Widgets} /> </Router>), document.getElementById('root'));

hver <Route> gengiver sin respektive komponent, når dens sti matcher URL ‘ en. Kun en af disse tre komponenter vil blive gengivet i 'root' til enhver tid. Med denne strategi monterer vi routeren til DOM 'root' en gang, så skifter routeren komponenter ind og ud med ruteændringer.

det er også værd at bemærke, at routeren skifter ruter uden at stille anmodninger til serveren, så forestil dig, at hver komponent kan være en helt ny side.

genanvendeligt Layout

vi begynder at se den ydmyge begyndelse af en enkelt Sideapplikation. Det løser dog stadig ikke problemer i den virkelige verden. Sikker på, vi kunne bygge de tre komponenter til at være fulde HTML-sider, men hvad med kode genbrug? Chancerne er, at disse tre komponenter deler fælles aktiver som en overskrift og sidebjælke, så hvordan forhindrer vi HTML-gentagelse i hver komponent?

lad os forestille os, at vi byggede en internetapp, der lignede denne mockup:

en simpel hjemmeside mockup.

når du begynder at tænke over, hvordan denne mockup kan opdeles i genanvendelige sektioner, kan du ende med denne ide:

hvordan du kan opdele den enkle net mockup i sektioner.

tænkning i form af nestable komponenter og layout vil give os mulighed for at skabe genanvendelige dele.

pludselig lader kunstafdelingen dig vide, at appen har brug for en side til søgning af kontroller, der ligner siden til søgning af brugere. Med Brugerliste og kontrolliste, der begge kræver det samme “look” til deres søgeside, giver ideen om at have søgelayout som en separat komponent endnu mere mening nu:

Søg efter Kontroller nu, i stedet for brugere, men de overordnede sektioner forbliver de samme.

Søg Layout kan være en overordnet skabelon for alle former for søgesider nu. Og mens nogle sider muligvis har brug for søgelayout, andre kan direkte bruge Hovedlayout uden det:

et layout afkoblet.

dette er en fælles strategi, og hvis du har brugt nogen templating system, du har sikkert gjort noget meget lignende. Lad os nu arbejde på HTML. For at starte, gør vi statisk HTML uden at overveje JavaScript:

<div> <!-- Main Layout --> <div class="app"> <header class="primary-header"><header> <aside class="primary-aside"></aside> <main> <!-- Search Layout --> <div class="search"> <header class="search-header"></header> <div class="results"> <!-- User List --> <ul class="user-list"> <li>Dan</li> <li>Ryan</li> <li>Michael</li> </ul> </div> <div class="search-footer pagination"></div> </div> </main> </div></div>

husk, at 'root' elementet altid vil være til stede, da det er det eneste element, som den oprindelige HTML-krop har, før JavaScript starter. Ordet” root ” er passende, fordi hele vores React-applikation vil montere den. Men der er ikke noget “rigtigt navn” eller konvention til det, du kalder det. Jeg har valgt “root”, så vi fortsætter med at bruge det gennem eksemplerne. Bare pas på, at montering direkte på <body> elementet er stærkt modløs.

når du har oprettet den statiske HTML, skal du konvertere den til React-komponenter:

var MainLayout = React.createClass({ render: function() { // Note the `className` rather than `class` // `class` is a reserved word in JavaScript, so JSX uses `className` // Ultimately, it will render with a `class` in the DOM return ( <div className="app"> <header className="primary-header"><header> <aside className="primary-aside"></aside> <main> {this.props.children} </main> </div> ); }});var SearchLayout = React.createClass({ render: function() { return ( <div className="search"> <header className="search-header"></header> <div className="results"> {this.props.children} </div> <div className="search-footer pagination"></div> </div> ); }});var UserList = React.createClass({ render: function() { return ( <ul className="user-list"> <li>Dan</li> <li>Ryan</li> <li>Michael</li> </ul> ); }});

bliv ikke for distraheret mellem det, jeg kalder “Layout” vs “komponent”. Alle tre af disse er React-komponenter. Jeg vælger bare at kalde to af dem” Layouts”, da det er den rolle, de udfører.

vi vil til sidst bruge “indlejrede ruter” til at placere UserList inde SearchLayout, derefter inde MainLayout. Men først skal du bemærke, at når UserList er placeret inde i sin forælder SearchLayout, vil forælderen bruge this.props.children til at bestemme dens placering. Alle komponenter har this.props.children som en prop, men det er kun, når komponenter er indlejret, at den overordnede komponent får denne prop fyldt automatisk af React. For komponenter, der ikke er overordnede komponenter, vil this.props.children være null.

indlejrede ruter

så hvordan får vi disse komponenter til at rede? Routeren gør det for os, når vi hekker ruter:

ReactDOM.render(( <Router> <Route component={MainLayout}> <Route component={SearchLayout}> <Route path="users" component={UserList} /> </Route> </Route> </Router>), document.getElementById('root'));

komponenter vil blive indlejret i overensstemmelse med, hvordan routeren reder sine ruter. Når brugeren besøger /users ruten, vil React Router placere UserList komponenten inde SearchLayoutog derefter begge inde MainLayout. Slutresultatet af at besøge /usersvil være de tre indlejrede komponenter placeret inde 'root'.

Bemærk, at vi ikke har en regel for, hvornår brugeren besøger startsidestien (/) eller ønsker at søge kontroller. De blev udeladt for enkelhed, men lad os sætte dem ind med den nye router:

ReactDOM.render(( <Router> <Route component={MainLayout}> <Route path="/" component={Home} /> <Route component={SearchLayout}> <Route path="users" component={UserList} /> <Route path="widgets" component={WidgetList} /> </Route> </Route> </Router>), document.getElementById('root'));

du har sikkert bemærket nu, at JS følger reglerne i den forstand, at Route komponenten enten kan skrives som et tag: <Route />eller to: <Route>...</Route>. Dette gælder for alle dine brugerdefinerede komponenter og normale DOM noder. For eksempel <div /> er gyldig og konverteres til <div></div>, når den gengives.

for korthed, forestil dig WidgetListligner UserList.

siden <Route component={SearchLayout}> har to underordnede ruter nu, kan brugeren besøge /users eller /widgets og den tilsvarende <Route> indlæser de respektive komponenter inde i SearchLayout komponenten.

Bemærk også, hvordan Home komponenten placeres direkte inde MainLayout uden at SearchLayouter involveret — på grund af hvordan <Route> s er indlejret. Du kan sikkert forestille dig, at det er nemt at omarrangere, hvordan layout og komponenter er indlejret ved at omarrangere ruterne.

Indeksruter

React Router er meget udtryksfuld, og ofte er der mere end en måde at gøre det samme på. For eksempel kunne vi også have skrevet ovenstående router som denne:

ReactDOM.render(( <Router> <Route path="/" component={MainLayout}> <IndexRoute component={Home} /> <Route component={SearchLayout}> <Route path="users" component={UserList} /> <Route path="widgets" component={WidgetList} /> </Route> </Route> </Router>), document.getElementById('root'));

på trods af dets forskellige udseende fungerer de begge på nøjagtig samme måde.

valgfri Ruteattributter

nogle gange vil <Route> have en component attribut uden path, som i SearchLayout ruten ovenfra. Andre gange kan det være nødvendigt at have en <Route> med en path og nej component. For at se hvorfor, lad os starte med dette eksempel:

<Route path="product/settings" component={ProductSettings} /><Route path="product/inventory" component={ProductInventory} /><Route path="product/orders" component={ProductOrders} />

/product delen af path er gentagen. Vi kan fjerne gentagelsen ved at indpakke alle tre ruter i en ny <Route>:

<Route path="product"> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>

igen viser React Router sin udtryksevne. Spørgsmål: Har du bemærket problemet med begge løsninger? I øjeblikket har vi ingen regler for, hvornår brugeren besøger /product stien.

for at løse dette kan vi tilføje en IndexRoute:

<Route path="product"> <IndexRoute component={ProductProfile} /> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>

brug <Link> ikke <a>

når du opretter ankre til dine ruter, skal du bruge <Link to="">i stedet for <a href="">. Bare rolig, når du bruger <Link> komponenten, vil React Router i sidste ende give dig et almindeligt anker i DOM. Brug af <Link> er dog nødvendigt for React Router at gøre noget af sin routing magi.

lad os tilføje nogle link (ankre) til vores MainLayout:

var MainLayout = React.createClass({ render: function() { return ( <div className="app"> <header className="primary-header"></header> <aside className="primary-aside"> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/users">Users</Link></li> <li><Link to="/widgets">Widgets</Link></li> </ul> </aside> <main> {this.props.children} </main> </div> ); }});

attributter på <Link> komponenter føres igennem til det anker, de opretter. Så denne JS:

<Link to="/users" className="users">

vil blive dette i DOM:

<a href="/users" class="users">

hvis du har brug for at oprette et anker til ikke-router-stier, såsom en ekstern hjemmeside, derefter bruge normale anker tags som sædvanlig. For mere information, se dokumentationen for Indeksrute og Link.

aktive Links

en cool funktion af <Link> komponenten er dens evne til at vide, hvornår den er aktiv:

<Link to="/users" activeClassName="active">Users</Link>

hvis brugeren er på /users stien, vil routeren opsøge matchende ankre, der blev lavet med <Link> og det vil skifte deres active klasse. Se mere om denne funktion.

bro.Serhistorik

for at forhindre forvirring har jeg udeladt en vigtig detalje indtil nu. <Router> skal vide, hvilken historiksporingsstrategi der skal bruges. React Router docs anbefaler gennemserhistorie, der implementeres som følger:

var browserHistory = ReactRouter.browserHistory;ReactDOM.render(( <Router history={browserHistory}> ... </Router>), document.getElementById('root'));

i tidligere versioner af React Router var attributten history ikke påkrævet, og standardværdien var at bruge hashHistory. Som navnet antyder, brugte det et # hash-tegn i URL ‘ en til at styre front-end SPA-stil routing, svarende til hvad du kunne forvente af en rygrad.JS router.

med hashHistory vil URL ‘ er se sådan ud:

  • example.com
  • example.com/#/users?_k=ckuvup
  • example.com/#/widgets?_k=ckuvup

Hvad sker der med de grimme forespørgselsstrenge?

når browserHistory implementeres, ser stierne mere organiske ud:

  • example.com
  • example.com/users
  • eksempel.com / kontroller

der er dog en advarsel på serveren, når browserHistory bruges i front-end. Hvis brugeren starter sit besøg på example.com og derefter navigerer til /users og /widgets, håndterer React Router dette scenario som forventet. Men hvis brugeren starter sit besøg ved at skrive example.com/widgets direkte i Bro.sereren, eller hvis de opdateres på example.com/widgets, skal bro. sereren stille mindst en anmodning til serveren for /widgets. Hvis der ikke er en server-side router selv, dette vil levere en 404:

vær forsigtig med URL ‘ er. Du skal bruge en server side router.

for at løse 404-problemet fra serveren anbefaler React Router en jokertegnrouter på serversiden. Med denne strategi, uanset hvilken server-side rute kaldes, serveren skal altid tjene den samme HTML-fil. Så hvis brugeren starter direkte på example.com/widgets, selvom den samme HTML-fil returneres, er React Router smart nok til at indlæse den korrekte komponent.

brugeren vil ikke bemærke noget underligt, men du har måske bekymringer om altid at servere den samme HTML-fil. I kodeeksempler vil denne serie fortsætte med at bruge strategien “jokertegnrouter”, men det er op til dig at håndtere din routing på serversiden på måder, du finder passende.

kan React Router bruges på både serversiden og klientsiden på en isomorf måde? Sikker på det kan, men det er langt ud over omfanget af denne tutorial.

omdirigere med browserHistory

browserHistory objektet er en singleton, så du kan inkludere det i nogen af dine filer. Hvis du har brug for manuelt at omdirigere brugeren i nogen af din kode, kan du bruge det push metode til at gøre det:

browserHistory.push('/some/path');

Rutetilpasning

React router håndterer rutetilpasning på samme måde som andre routere:

<Route path="users/:userId" component={UserProfile} />

denne rute matcher, når brugeren besøger en sti, der starter med users/ og har nogen værdi bagefter. Det vil matche /users/1, /users/143 eller endda /users/abc (som du skal validere på egen hånd).

React Router vil passere værdien for :userIdsom en prop til UserProfile. Denne rekvisitter er adgang som this.props.params.userId inde UserProfile.

Router Demo

på dette tidspunkt har vi nok kode til at vise en demo.

se pennen React-Router Demo af Brad Vestfall (@Brad vestfall) på CodePen.

hvis du klikker på et par ruter i eksemplet, kan du bemærke, at bro.sererens tilbage og frem-knapper fungerer sammen med routeren. Dette er en af hovedårsagerne til, at disse history strategier findes. Husk også, at med hver rute, du besøger, er der ingen anmodninger til serveren undtagen den allerførste til at få den oprindelige HTML. Hvor sejt er det?

ES6

i Vores CodePen-eksempel er React, ReactDOM og ReactRouter globale variabler fra et CDN. Inde i ReactRouter objektet er alle slags ting, vi har brug for som Router og Route komponenterne. Så vi kunne bruge ReactRouter sådan her:

ReactDOM.render(( <ReactRouter.Router> <ReactRouter.Route ... /> </ReactRouter.Router>), document.getElementById('root'));

her skal vi præfiks alle vores routerkomponenter med deres overordnede objekt ReactRouter. Eller vi kunne bruge ES6S nye destruktureringssyntaks som denne:

var { Router, Route, IndexRoute, Link } = ReactRouter

dette “udtrækker” dele af ReactRouter i normale variabler, så vi kan få adgang til dem direkte.

fra nu af vil eksemplerne i denne serie bruge en række ES6-syntakser, herunder destrukturering, spredningsoperatøren, import/eksport og måske andre. Der vil være en kort forklaring på hver nye syntaks, når de vises, og GitHub-repoen, der følger med denne serie, har også masser af ES6-forklaringer.

Bundling med netpakke og Babel

som tidligere nævnt kommer denne serie med en GitHub repo, så du kan eksperimentere med kode. Da det vil ligne skabelsen af et SPA i den virkelige verden, vil det bruge værktøjer som Babel og Babel.

  • pakker flere JavaScript-filer i en fil.
  • Babel konverterer ES6 (ES2015) kode til ES5, da de fleste brugere endnu ikke forstår alle ES6. Efterhånden som denne artikel bliver ældre, understøtter vi ES6, og Babel er muligvis ikke nødvendig.

hvis du ikke er for komfortabel med at bruge disse værktøjer endnu, skal du ikke bekymre dig, eksempelkoden har ALT setup allerede, så du kan fokusere på React. Men sørg for at gennemgå eksempelkoden README.md fil for yderligere dokumentation arbejdsgang.

vær forsigtig med forældet syntaks

søgning på Google efter information om React Router kan lande dig på en af de mange artikler eller Stackoverløbssider, der blev skrevet, da React Router var i sin pre-1.0-udgivelse. Mange funktioner fra pre-1.0 frigivelse er forældet nu. Her er en kort liste:

  • <Route name="" /> er forældet. Brug <Route path="" /> i stedet.
  • <Route handler="" /> er forældet. Brug <Route component="" /> i stedet.
  • <NotFoundRoute /> er forældet. Se alternativ
  • <RouteHandler /> er forældet.
  • willTransitionTo er forældet. Se onEnter
  • willTransitionFrom er forældet. Se onLeave
  • “placeringer” kaldes nu “historier”.

se den fulde liste for 1.0.0 og 2.0.0

Resume

der er stadig flere funktioner i React Router, der ikke blev vist, så sørg for at tjekke API Docs. Skaberne af React Router har også oprettet en trinvis vejledning til React Router og også checkout denne React.js Conf Video om, hvordan React Router blev oprettet.

særlig tak til Lynn Fisher for kunstværket @lynnandtonic

artikelserie:

  1. React Router (du er her!)
  2. Beholderkomponenter
  3. Reduks

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.