Leveling up with React: React Router

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:

  1. React Router (Sie sind hier!)
  2. Container-Komponenten
  3. 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:

Ein einfaches Website-Modell.

Wenn Sie darüber nachdenken, wie dieses Modell in wiederverwendbare Abschnitte unterteilt werden kann, erhalten Sie möglicherweise diese Idee:

Wie Sie das einfache Web-Modell in Abschnitte aufteilen können.

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:

Suchen Sie jetzt nach Widgets anstelle von Benutzern, aber die übergeordneten Abschnitte bleiben gleich.

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:

Ein Layout entkoppelt.

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:

Vorsicht mit URLs. Sie benötigen einen serverseitigen Router.

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:

  1. React Router (Sie sind hier!)
  2. Container-Komponenten
  3. Redux

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.