Dieses Tutorial ist das erste einer dreiteiligen Serie über React von Brad Westfall. Als Brad mir das vorstellte, wies er darauf hin, dass es eine Menge Tutorials zum Einstieg in React gibt, aber nicht so viel darüber, wohin man von dort aus gehen soll. Wenn Sie ganz neu in React sind, empfehle ich Ihnen, dieses Intro-Video anzusehen. Diese Serie setzt dort an, wo die Grundlagen aufhören.
- Artikelserie:
- Seriencode
- React-Router
- Mehrere Routen
- Wiederverwendbares Layout
- Verschachtelte Routen
- IndexRoutes
- Optionale Routenattribute
- Verwenden Sie <Link> nicht <a>
- Aktive Links
- Browserverlauf
- Umleiten mit browserHistory
- Routenanpassung
- Router Demo
- ES6
- Bündelung mit Webpack und Babel
- Seien Sie vorsichtig mit veralteter Syntax
- Zusammenfassung
- Artikelserie:
Artikelserie:
- React Router (Sie sind hier!)
- Container-Komponenten
- Redux
Warnung! Dieser Artikel wurde vor React Router 4 geschrieben, der zu einer Standardauswahl für das Routing in React geworden ist. Hier gibt es einen neuen Artikel zu React Router 4, den Sie unbedingt lesen sollten.
Als ich zum ersten Mal lernte, fand ich viele Anfängerleitfäden (dh 1, 2, 3, 4), die zeigten, wie einzelne Komponenten erstellt und im DOM gerendert werden. Sie haben gute Arbeit geleistet, um die Grundlagen wie JSX und Requisiten zu vermitteln, aber ich hatte Mühe herauszufinden, wie React im Großen und Ganzen funktioniert – wie eine reale Single Page Application (SPA). Da diese Serie viel Material abdeckt, werden die Konzepte für absolute Anfänger nicht behandelt. Stattdessen wird davon ausgegangen, dass Sie bereits verstehen, wie Sie mindestens eine Komponente erstellen und rendern.
Für das, was es wert ist, sind hier einige andere großartige Guides, die sich an Anfänger richten:
- Reagieren.js und wie passt es zu allem anderen?
- Rethinking (Industry) Best Practices
- Reagieren.js Einführung Für Leute, die gerade genug jQuery kennen, um durchzukommen
Seriencode
Diese Serie enthält auch Code zum Spielen auf GitHub. Während der gesamten Serie werden wir ein grundlegendes SPA erstellen, das sich auf Benutzer und Widgets konzentriert.
Um die Dinge einfach und kurz zu halten, gehen die Beispiele in dieser Reihe zunächst davon aus, dass React und React Router von einem CDN abgerufen werden. Sie werden also require()
oder import
in den unmittelbaren Beispielen unten nicht sehen. Gegen Ende dieses Tutorials werden wir jedoch Webpack und Babel für die GitHub-Anleitungen vorstellen. An diesem Punkt ist es alles ES6!
React-Router
React ist kein Framework, sondern eine Bibliothek. Daher werden nicht alle Anforderungen einer Anwendung erfüllt. Es leistet hervorragende Arbeit beim Erstellen von Komponenten und beim Bereitstellen eines Systems zum Verwalten des Status, aber das Erstellen eines komplexeren SPA erfordert eine unterstützende Besetzung. Das erste, was wir uns ansehen werden, ist der Router.
Wenn Sie zuvor einen Front-End-Router verwendet haben, sind viele dieser Konzepte vertraut. Aber im Gegensatz zu jedem anderen Router, den ich zuvor verwendet habe, verwendet React Router JSX, was auf den ersten Blick etwas seltsam aussehen könnte.
Als Grundierung ist es so, eine einzelne Komponente zu rendern:
var Home = React.createClass({ render: function() { return (<h1>Welcome to the Home Page</h1>); }});ReactDOM.render(( <Home />), document.getElementById('root'));
So würde die Home
-Komponente mit React Router gerendert:
...ReactDOM.render(( <Router> <Route path="/" component={Home} /> </Router>), document.getElementById('root'));
Beachten Sie, dass <Router>
und <Route>
zwei verschiedene Dinge sind. Sie sind technisch React-Komponenten, aber sie erstellen DOM selbst nicht. Während es so aussehen mag, als würde der <Router>
selbst auf den 'root'
gerendert, definieren wir eigentlich nur Regeln darüber, wie unsere Anwendung funktioniert. In Zukunft werden Sie dieses Konzept oft sehen: komponenten existieren manchmal nicht, um DOM selbst zu erstellen, sondern um andere Komponenten zu koordinieren, die dies tun.
Im Beispiel definiert <Route>
eine Regel, nach der der Besuch der Homepage /
die Home
-Komponente in 'root'
rendert.
Mehrere Routen
Im vorherigen Beispiel ist die einzelne Route sehr einfach. Es gibt uns nicht viel Wert, da wir bereits die Home
Komponente rendern konnten, ohne dass der Router beteiligt war.
Die Leistung des Routers kommt zum Tragen, wenn wir mehrere Routen verwenden, um zu definieren, welche Komponente basierend auf dem aktuell aktiven Pfad gerendert werden soll:
ReactDOM.render(( <Router> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> <Route path="/widgets" component={Widgets} /> </Router>), document.getElementById('root'));
Jedes <Route>
rendert seine jeweilige Komponente, wenn sein Pfad mit der URL übereinstimmt. Nur eine dieser drei Komponenten wird zu einem bestimmten Zeitpunkt in 'root'
gerendert. Mit dieser Strategie mounten wir den Router einmal im DOM 'root'
, dann tauscht der Router Komponenten mit Routenänderungen ein und aus.
Es ist auch erwähnenswert, dass der Router Routen wechselt, ohne Anforderungen an den Server zu stellen.
Wiederverwendbares Layout
Wir beginnen die bescheidenen Anfänge einer einseitigen Anwendung zu sehen. Es löst jedoch immer noch keine realen Probleme. Sicher, wir könnten die drei Komponenten als vollständige HTML-Seiten erstellen, aber was ist mit der Wiederverwendung von Code? Die Chancen stehen gut, dass diese drei Komponenten gemeinsame Assets wie eine Kopfzeile und eine Seitenleiste teilen, also wie verhindern wir HTML-Wiederholung in jeder Komponente?
Stellen wir uns vor, wir würden eine Web-App erstellen, die diesem Modell ähnelt:
Wenn Sie darüber nachdenken, wie dieses Modell in wiederverwendbare Abschnitte unterteilt werden kann, erhalten Sie möglicherweise diese Idee:
Das Denken in Bezug auf verschachtelbare Komponenten und Layouts ermöglicht es uns, wiederverwendbare Teile zu erstellen.
Plötzlich teilt Ihnen die Kunstabteilung mit, dass die App eine Seite zum Suchen von Widgets benötigt, die der Seite zum Suchen von Benutzern ähnelt. Da Benutzerliste und Widget-Liste beide dasselbe „Aussehen“ für ihre Suchseite benötigen, ist die Idee, das Suchlayout als separate Komponente zu verwenden, jetzt noch sinnvoller:
Das Suchlayout kann jetzt eine übergeordnete Vorlage für alle Arten von Suchseiten sein. Und während einige Seiten möglicherweise ein Suchlayout benötigen, können andere das Hauptlayout direkt ohne es verwenden:
Dies ist eine gängige Strategie, und wenn Sie ein Template-System verwendet haben, haben Sie wahrscheinlich etwas sehr Ähnliches getan. Jetzt arbeiten wir am HTML. Zu Beginn machen wir statisches HTML ohne Berücksichtigung von 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>
Denken Sie daran, dass das 'root'
-Element immer vorhanden ist, da es das einzige Element ist, das der ursprüngliche HTML-Text enthält, bevor JavaScript gestartet wird. Das Wort „root“ ist angemessen, da unsere gesamte React-Anwendung darauf bereitgestellt wird. Aber es gibt keinen „richtigen Namen“ oder Konvention zu dem, was Sie es nennen. Ich habe „root“ gewählt, daher werden wir es in den Beispielen weiterhin verwenden. Beachten Sie jedoch, dass von der direkten Montage an das <body>
-Element dringend abgeraten wird.
Nachdem Sie den statischen HTML-Code erstellt haben, konvertieren Sie ihn in React-Komponenten:
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> ); }});
Lassen Sie sich nicht zu sehr ablenken zwischen dem, was ich „Layout“ vs „Komponente“ nenne. Alle drei sind React-Komponenten. Ich nenne nur zwei von ihnen „Layouts“, da dies die Rolle ist, die sie ausführen.
Wir werden schließlich „verschachtelte Routen“ verwenden, um UserList
in SearchLayout
und dann in MainLayout
zu platzieren. Beachten Sie jedoch zunächst, dass wenn UserList
in seinem übergeordneten SearchLayout
platziert wird, das übergeordnete this.props.children
verwendet, um seine Position zu bestimmen. Alle Komponenten haben this.props.children
als Requisite, aber nur wenn Komponenten verschachtelt sind, wird diese Requisite von der übergeordneten Komponente automatisch von React gefüllt. Für Komponenten, die keine übergeordneten Komponenten sind, ist this.props.children
null
.
Verschachtelte Routen
Wie bringen wir diese Komponenten zum Verschachteln? Der Router erledigt dies für uns, wenn wir Routen verschachteln:
ReactDOM.render(( <Router> <Route component={MainLayout}> <Route component={SearchLayout}> <Route path="users" component={UserList} /> </Route> </Route> </Router>), document.getElementById('root'));
Komponenten werden entsprechend der Verschachtelung der Routen durch den Router verschachtelt. Wenn der Benutzer die /users
-Route besucht, platziert der Router die UserList
-Komponente in SearchLayout
und dann beide in MainLayout
. Das Endergebnis des Besuchs von /users
sind die drei verschachtelten Komponenten in 'root'
.
Beachten Sie, dass wir keine Regel haben, wann der Benutzer den Homepagepfad (/
) besucht oder Widgets durchsuchen möchte. Diese wurden der Einfachheit halber weggelassen, aber lassen Sie uns sie mit dem neuen Router einfügen:
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'));
Sie haben wahrscheinlich inzwischen bemerkt, dass JSX XML-Regeln in dem Sinne folgt, dass die Route
-Komponente entweder als ein Tag geschrieben werden kann: <Route />
oder zwei: <Route>...</Route>
. Dies gilt für alle JSX einschließlich Ihrer benutzerdefinierten Komponenten und normalen DOM-Knoten. Zum Beispiel ist <div />
gültiges JSX und wird beim Rendern in <div></div>
konvertiert.
Stellen Sie sich der Kürze halber vor, WidgetList
ähnelt dem UserList
.
Da <Route component={SearchLayout}>
jetzt zwei untergeordnete Routen hat, kann der Benutzer /users
oder /widgets
besuchen und das entsprechende <Route>
lädt seine jeweiligen Komponenten in die SearchLayout
-Komponente.
Beachten Sie auch, wie die Home
—Komponente direkt in MainLayout
platziert wird, ohne dass SearchLayout
beteiligt ist – aufgrund der Verschachtelung der <Route>
. Sie können sich wahrscheinlich vorstellen, dass es einfach ist, die Verschachtelung von Layouts und Komponenten neu anzuordnen, indem Sie die Routen neu anordnen.
IndexRoutes
React Router ist sehr ausdrucksstark und oft gibt es mehr als eine Möglichkeit, dasselbe zu tun. Zum Beispiel hätten wir den obigen Router auch so schreiben können:
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'));
Trotz ihres unterschiedlichen Aussehens funktionieren beide genau gleich.
Optionale Routenattribute
Manchmal hat <Route>
ein component
-Attribut ohne path
, wie in der SearchLayout
-Route von oben. In anderen Fällen kann es erforderlich sein, einen <Route>
mit einem path
und keinem component
zu haben. Um zu sehen, warum, beginnen wir mit diesem Beispiel:
<Route path="product/settings" component={ProductSettings} /><Route path="product/inventory" component={ProductInventory} /><Route path="product/orders" component={ProductOrders} />
Der /product
-Teil des path
wiederholt sich. Wir können die Wiederholung entfernen, indem wir alle drei Routen in eine neue einwickeln <Route>
:
<Route path="product"> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>
Auch hier zeigt der Router seine Ausdruckskraft. Quiz: Haben Sie das Problem mit beiden Lösungen bemerkt? Im Moment haben wir keine Regeln dafür, wann der Benutzer den Pfad /product
besucht.
Um dies zu beheben, können wir eine IndexRoute
:
<Route path="product"> <IndexRoute component={ProductProfile} /> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>
Verwenden Sie <Link>
nicht <a>
Wenn Sie Anker für Ihre Routen erstellen, müssen Sie <Link to="">
anstelle von <a href="">
verwenden. Keine Sorge, wenn Sie die <Link>
-Komponente verwenden, gibt Ihnen React Router letztendlich einen normalen Anker im DOM. Die Verwendung von <Link>
ist jedoch erforderlich, damit der Router einen Teil seiner Routing-Magie ausführen kann.
Fügen wir einen Link (Anker) zu unserem MainLayout
hinzu:
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> ); }});
Attribute für <Link>
Komponenten werden an den Anker übergeben, den sie erstellen. Also das JSX:
<Link to="/users" className="users">
Wird dies in DOM werden:
<a href="/users" class="users">
Wenn Sie einen Anker für Nicht-Router-Pfade erstellen müssen, z. B. eine externe Website, verwenden Sie wie gewohnt normale Anker-Tags. Weitere Informationen finden Sie in der Dokumentation zu IndexRoute und Link.
Aktive Links
Ein cooles Feature der <Link>
-Komponente ist ihre Fähigkeit zu wissen, wann sie aktiv ist:
<Link to="/users" activeClassName="active">Users</Link>
Wenn sich der Benutzer auf dem Pfad /users
befindet, sucht der Router nach passenden Ankern, die mit <Link>
erstellt wurden, und schaltet ihre active
-Klasse um. Weitere Informationen zu dieser Funktion.
Browserverlauf
Um Verwirrung zu vermeiden, habe ich bis jetzt ein wichtiges Detail ausgelassen. Der <Router>
muss wissen, welche Verlaufsverfolgungsstrategie verwendet werden soll. React Router-Dokumente empfehlen browserHistory, das wie folgt implementiert ist:
var browserHistory = ReactRouter.browserHistory;ReactDOM.render(( <Router history={browserHistory}> ... </Router>), document.getElementById('root'));
In früheren Versionen von React Router war das Attribut history
nicht erforderlich und die Standardeinstellung war hashHistory . Wie der Name schon sagt, wurde ein #
-Hash-Zeichen in der URL verwendet, um das Front-End-Routing im SPA-Stil zu verwalten, ähnlich wie Sie es von einem Backbone erwarten.js-Router.
Mit hashHistory
sehen URLs folgendermaßen aus:
- example.com
- example.com/#/users?_k=ckuvup
- example.com/#/widgets?_k=ckuvup
Was ist mit diesen hässlichen Abfragezeichenfolgen los?
Wenn browserHistory
implementiert ist, sehen die Pfade organischer aus:
- example.com
- example.com/users
- Beispiel.com /widgets
Es gibt jedoch eine Einschränkung auf dem Server, wenn browserHistory
im Front-End verwendet wird. Wenn der Benutzer seinen Besuch bei example.com
beginnt und dann zu /users
und /widgets
navigiert, behandelt der Router dieses Szenario wie erwartet. Wenn der Benutzer seinen Besuch jedoch beginnt, indem er example.com/widgets
direkt in den Browser eingibt, oder wenn er auf example.com/widgets
aktualisiert, muss der Browser mindestens eine Anfrage an den Server für /widgets
stellen. Wenn es jedoch keinen serverseitigen Router gibt, liefert dies eine 404:
Um das 404-Problem vom Server aus zu lösen, empfiehlt React Router einen Platzhalterrouter auf der Serverseite. Bei dieser Strategie sollte der Server unabhängig von der serverseitigen Route immer dieselbe HTML-Datei bereitstellen. Wenn der Benutzer dann direkt bei example.com/widgets
startet, obwohl dieselbe HTML-Datei zurückgegeben wird, ist der Router intelligent genug, um die richtige Komponente zu laden.
Der Benutzer wird nichts Seltsames bemerken, aber Sie haben möglicherweise Bedenken, immer dieselbe HTML-Datei bereitzustellen. In Codebeispielen wird diese Serie weiterhin die „Wildcard Router“ -Strategie verwenden, aber es liegt an Ihnen, Ihr serverseitiges Routing so zu handhaben, wie Sie es für richtig halten.
Kann React Router sowohl serverseitig als auch clientseitig isomorph verwendet werden? Sicher kann es, aber das geht weit über den Rahmen dieses Tutorials hinaus.
Umleiten mit browserHistory
Das browserHistory
-Objekt ist ein Singleton, sodass Sie es in jede Ihrer Dateien aufnehmen können. Wenn Sie den Benutzer in einem Ihrer Codes manuell umleiten müssen, können Sie dazu die push
-Methode verwenden:
browserHistory.push('/some/path');
Routenanpassung
React Router behandelt die Routenanpassung ähnlich wie andere Router:
<Route path="users/:userId" component={UserProfile} />
Diese Route stimmt überein, wenn der Benutzer einen Pfad besucht, der mit users/
beginnt und danach einen beliebigen Wert hat. Es stimmt mit /users/1
, /users/143
oder sogar /users/abc
überein (was Sie selbst validieren müssen).
React Router übergibt den Wert für :userId
als Requisite an UserProfile
. Auf diese Requisiten wird als this.props.params.userId
innerhalb von UserProfile
zugegriffen.
Router Demo
An dieser Stelle haben wir genug Code, um eine Demo zu zeigen.
Siehe die Pen React-Router-Demo von Brad Westfall (@bradwestfall) auf CodePen.
Wenn Sie im Beispiel auf einige Routen geklickt haben, stellen Sie möglicherweise fest, dass die Schaltflächen Zurück und Vorwärts des Browsers mit dem Router funktionieren. Dies ist einer der Hauptgründe, warum diese history
-Strategien existieren. Denken Sie auch daran, dass bei jeder Route, die Sie besuchen, keine Anforderungen an den Server gestellt werden, außer der allerersten, um den ursprünglichen HTML-Code abzurufen. Wie cool ist das?
ES6
In unserem CodePen-Beispiel sind React
, ReactDOM
und ReactRouter
globale Variablen aus einem CDN. Innerhalb des ReactRouter
-Objekts befinden sich alle möglichen Dinge, die wir benötigen, wie die Komponenten Router
und Route
. Wir könnten also ReactRouter
wie folgt verwenden:
ReactDOM.render(( <ReactRouter.Router> <ReactRouter.Route ... /> </ReactRouter.Router>), document.getElementById('root'));
Hier müssen wir allen unseren Router-Komponenten ihr übergeordnetes Objekt ReactRouter
voranstellen. Oder wir könnten die neue Destrukturierungssyntax von ES6 wie folgt verwenden:
var { Router, Route, IndexRoute, Link } = ReactRouter
Dies „extrahiert“ Teile von ReactRouter
in normale Variablen, damit wir direkt darauf zugreifen können.
Ab jetzt werden die Beispiele in dieser Reihe eine Vielzahl von ES6-Syntaxen verwenden, einschließlich Destrukturierung, Spread-Operator, Importe / Exporte und möglicherweise andere. Es wird eine kurze Erklärung jeder neuen Syntax geben, sobald sie erscheint, und das GitHub-Repo, das mit dieser Serie geliefert wird, enthält auch viele ES6-Erklärungen.
Bündelung mit Webpack und Babel
Wie bereits erwähnt, enthält diese Serie ein GitHub-Repo, mit dem Sie mit Code experimentieren können. Da es den Anforderungen eines realen SPA ähnelt, werden Tools wie Webpack und Babel verwendet.
- Webpack bündelt mehrere JavaScript-Dateien in einer Datei für den Browser.
- Babel konvertiert ES6 (ES2015) -Code in ES5, da die meisten Browser ES6 noch nicht vollständig verstehen. Da dieser Artikel älter wird, unterstützen Browser ES6 und Babel wird möglicherweise nicht benötigt.
Wenn Sie mit diesen Tools noch nicht allzu vertraut sind, machen Sie sich keine Sorgen, der Beispielcode hat bereits alles eingerichtet, sodass Sie sich auf React konzentrieren können. Überprüfen Sie jedoch unbedingt den Beispielcode README.md datei für zusätzliche Workflow-Dokumentation.
Seien Sie vorsichtig mit veralteter Syntax
Wenn Sie bei Google nach Informationen zu React Router suchen, gelangen Sie möglicherweise auf einen der vielen Artikel oder StackOverflow-Seiten, die geschrieben wurden, als sich React Router in der Version vor 1.0 befand. Viele Features aus der Pre-1.0 Release sind jetzt veraltet. Hier ist eine kurze Liste:
-
<Route name="" />
ist veraltet. Verwenden Sie stattdessen<Route path="" />
. -
<Route handler="" />
ist veraltet. Verwenden Sie stattdessen<Route component="" />
. -
<NotFoundRoute />
ist veraltet. Siehe Alternative -
<RouteHandler />
ist veraltet. -
willTransitionTo
ist veraltet. Siehe onEnter -
willTransitionFrom
ist veraltet. Siehe onLeave - „Standorte“ werden jetzt „Historien“ genannt.
Siehe die vollständige Liste für 1.0.0 und 2.0.0
Zusammenfassung
Es gibt noch mehr Funktionen in React Router, die nicht angezeigt wurden. Die Ersteller von React Router haben auch ein Schritt-für-Schritt-Tutorial für React Router erstellt und auch diese Reaktion überprüft.js Conf Video, wie React Router erstellt wurde.
Besonderer Dank geht an Lynn Fisher für das Artwork @lynnandtonic
Artikelserie:
- React Router (Sie sind hier!)
- Container-Komponenten
- Redux