Routing in einer großen React-Anwendung


Hallo, mein Name ist Boris Shabanov , ich bin Leiter der Frontend-Entwicklung in der Abteilung für die Entwicklung von Werbetechnologien der Rambler-Gruppe. Heute werde ich Ihnen erzählen, wie Routing-Probleme in unserer Anwendung aufgetreten sind und wie wir sie gelöst haben.


Advertising Technologies ist eine große Abteilung in der Rambler Group, die einen vollständigen Stapel von Werbetechnologien (SSP, DMP, DSP) implementiert. Für Endbenutzer, nämlich Werbetreibende und Agenturen, bieten wir eine praktische Oberfläche namens Summer , über die sie ihre Werbekampagnen starten, Statistiken über sie anzeigen und alles verwalten können.



Die Sommer- Oberfläche besteht aus einer Reihe von Bildschirmen, Formulare, die durch Links miteinander verbunden sind. An einem Ort kann der Benutzer mit wenigen Klicks die Informationen finden, die er benötigt.



Nach mehreren Jahren des Betriebs wurden die Projektanforderungen ernster und wir erkannten, dass wir dies beheben mussten. Da das Projekt von Anfang an auf React basierte, haben wir die React Create App als Grundlage für die neue Version verwendet. Und GraphQL - für die Interaktion mit dem Server, nämlich - Apollo-Client . Darüber hinaus verfügt die Abteilung über viel Fachwissen.


Aber wir hatten eine Frage, und was kann verwendet werden, um unsere Bewerbung weiterzuleiten? Wir haben bei Google nach einer Lösung mit den Worten "Reagieren" und "Router" gesucht . Auf den ersten fünf Seiten haben wir lediglich einen React Router gefunden . "Na gut ..." - wir dachten, kluge Leute empfehlen, wir müssen nehmen. Nach kurzer Zeit wurden die Fragen nicht weniger. Nehmen Sie zum Beispiel ein einfaches Beispiel für die Verwendung eines React-Routers:


import React from "react"; import { BrowserRouter as Router, Route, Link } from "react-router-dom"; const ParamsExample = () => ( <Router> <div> <h2>Accounts</h2> <ul> <li> <Link to="/netflix">Netflix</Link> </li> <li> <Link to="/zillow-group">Zillow Group</Link> </li> <li> <Link to="/yahoo">Yahoo</Link> </li> <li> <Link to="/modus-create">Modus Create</Link> </li> </ul> <Route path="/:id" component={Child} /> <Route path="/order/:direction" component={ComponentWithRegex} /> </div> </Router> ); 

Fragen stellen sich:


  • Was ist mit einer URL mit vielen Parametern?
  • Was kann ich tun, wenn der Produktmanager eintrifft und mich auffordert, die Seiten-URL in eine "freundlichere" zu ändern? "Carpet Commit" während des gesamten Projekts will das nicht wirklich.

Während des Entwicklungsprozesses begannen wir zu überlegen und nach Lösungen zu suchen, aber gleichzeitig lebten wir noch auf React-Router .


Im nächsten Sprint haben wir den Abschnitt "Hilfe" zur Arbeit genommen. Keine komplizierte Seite, und innerhalb weniger Stunden haben wir es geschafft.


Der springende Punkt ist jedoch, dass "Hilfe" ein solcher Abschnitt ist, auf den alle anderen Seiten verweisen, und es gibt viele Links dazu. Und der Link zu jeder Antwort wird dynamisch generiert. Für uns war dies ein weiterer Grund, über einen Routerwechsel nachzudenken.


Nach umfangreichen Recherchen im Internet haben wir eine akzeptable Lösung gefunden - Router5 .



Router5 ist eine Bibliothek, die nichts mit React zu tun hat. Sie können sie mit Angular, mit jQuery und mit allem verwenden. Es ist gleich zu erwähnen, dass Router5 keine Fortsetzung von React-Router @ 4 ist. Es handelt sich um zwei verschiedene Dinge, die von verschiedenen Personen entwickelt wurden und in keiner Weise miteinander verbunden sind.


Dementsprechend haben wir bei der Auswahl einer Bibliothek untersucht, wie beliebt sie ist und ob es überhaupt Sinn macht, sie zu verwenden. Wenn Sie mit der Anzahl der Sterne auf Github vergleichen, dann ist der Vorteil gegenüber React-Router.



Aber wir haben beschlossen, ein Risiko einzugehen. Wir haben uns angesehen, wie das Projekt lebt, ob jemand an seiner Entwicklung beteiligt ist, haben gesehen, dass Menschen neue Dinge dazu bringen, und das Projekt ist sehr lebendig.



Aus der Dokumentation können Sie herausfinden, dass er eine Reihe von Integrationen mit verschiedenen Frameworks und Bibliotheken hat. In der Dokumentation wird insbesondere die Integration mit React und Redux beschrieben. Wenn Sie jedoch gut suchen, finden Sie dort Beispiele mit mobX.



Wie werden Schnittstellen erstellt und wie werden Router in Router5 erstellt?
Die Geschichte ist hier dieselbe wie im React-Router: Jede Route ist ein Container für ihre Kinder.


Angenommen, Sie haben eine Website, die aus einer Zielseite besteht (wir haben sie zu Hause), und es gibt ein bestimmtes Admin-Panel, das aus zwei Abschnitten besteht - einer Liste der Benutzer und einer Liste der Gruppen dieser Benutzer. Wir können dies alles so konfigurieren, dass unser Home-Bereich wie eine Landing Page aussieht. Ferner wird der Admin-Abschnitt einen Wrapper für alle seine untergeordneten Seiten und Elemente haben, d. H. besteht zum Beispiel aus einer Fußzeile und einer Kopfzeile. Alle Unterseiten, die sich bereits im Administrationsbereich befinden, werden in diese Fußzeile und Kopfzeile umgewandelt.



Für eine solche Site lautet die Router5-Konfiguration wie folgt:


 import createRouter from 'router5'; import browserPlugin from 'router5/plugins/browser'; const routes = [ { name : 'home', }, { name : 'admin', children : [ { name : 'roles', }, { name : 'users', }, ] }, ]; const options = { defaultRoute: 'home', // ... }; const router = createRouter(routes, options) .usePlugin(browserPlugin()); router.start(); 

Im Beispiel haben wir eine einfache Option, aber tatsächlich können Sie viel mehr aus der Dokumentation lernen und ein bequemeres Format für sich selbst auswählen.


Wie ich bereits sagte, verwenden wir React für unsere Projekte in der Abteilung, daher werde ich weiterhin über React sprechen und Beispiele zeigen. Das folgende Codebeispiel zeigt, wie ein Projekt mit Router @ 5 ausgelöst wird.


 // app.js import ReactDOM from 'react-dom' import React from 'react' import App from './App' import { RouterProvider } from 'react-router5' import createRouter from './create-router' const router = createRouter() router.start(() => { ReactDOM.render(( <RouterProvider router={router}> <App /> </RouterProvider> ), document.getElementById('root')) }) 

 // Main.js import React from 'react' import { routeNode } from 'react-router5' import { UserView, UserList, NotFound } from './components'​ function Users(props) { const { previousRoute, route } = props​ switch (route.name) { case 'users.list': return <UserList /> case 'users.view': return <UserView /> default: return <NotFound /> } }​ export default routeNode('users')(Users) 

Weiter interessanter. Was uns gefallen hat und sehr elegant in das Projekt passt, ist, wie Übergänge gemacht werden. Angenommen, wir haben eine Site mit der oben beschriebenen Struktur. Und unser Benutzer möchte von der Zielseite zum Abschnitt "Benutzerliste" wechseln. Was macht router5 in diesem Fall? Zunächst wird der Home-Bereich deaktiviert und die entsprechenden Ereignisse ausgelöst. Ereignisse können sowohl in der Route selbst als auch in der Middleware verarbeitet werden, wo alle diese Übergänge verarbeitet werden können. Als Nächstes werden Aktivierungsereignisse für die Routen admin und admin.users generiert.



In unserer Anwendung wird die gesamte Middleware an einem Ort gesammelt. Hierbei handelt es sich um Middleware, die für das Laden der Komponenten (wir versuchen, die Anwendung in Teile zu schneiden und die Teile zu laden, die der Benutzer jetzt benötigt), die Lokalisierung, das Abrufen von Daten vom Server, das Überprüfen der Zugriffsrechte und das Sammeln von Übergangsanalysen verantwortlich ist. Infolgedessen denken Entwickler nicht einmal darüber nach, wie sie Daten empfangen, Berechtigungen überprüfen und was auf dem Bildschirm angezeigt werden soll. Sie machen den nächsten Abschnitt und Router5 löst alle Probleme.


Ein weiteres Problem, das wir ein für alle Mal lösen wollten, ist das Laden der Anwendung, aufgeteilt in verschiedene Teile. Meiner Meinung nach ähnelt das Herunterladen einer Anwendung mit React-Router den Abenteuern von Hedgehog im Nebel. Wenn Sie in jeder Phase einige Teile und Daten laden, ist Ihre Anwendung nicht immun gegen Fehler beim Empfangen von Daten vom Server. In diesem Fall müssen Ihre Designer den Status der Bildschirme für jede Notsituation ermitteln. Es scheint, dass an dieser Stelle die Wahrscheinlichkeit eines Entwicklerfehlers hoch ist (er kann es einfach vergessen), und diese Risiken sollten minimiert werden.


In Router5 wurde dies durch die Tatsache entschieden, dass jeder Übergang transaktional ausgeführt wird und die Änderung des visuellen Teils erst dann erfolgt, wenn die gesamte Middleware ausgearbeitet wurde und der Router davon überzeugt ist, dass unsere Anwendung das ziehen kann, was wir wollen. Dieser Ansatz hat seine Vor- und Nachteile, aber in unserem Fall gab es mehr Vor- und Nachteile. Das Problem mit der Ladeanzeige wird sofort behoben. Unsere Anwendung verwendet eine Download-Anzeige, die vom Router gesteuert wird.


Leider wurde die Arbeit mit Middleware vor etwa 3 oder 4 Monaten in der Dokumentation nicht vollständig offengelegt. Wir haben uns jedoch mit Problemen befasst und ein großartiges Beispiel gefunden, das zeigt, wie eine Anwendung mit React, Redux und Router5 bereitgestellt und erstellt wird.


Jede der URLs unserer Anwendung speichert einen bestimmten Datensatz, den wir zum Anzeigen von Daten benötigen (Bezeichner, zusätzliche Datenfilteroptionen usw.). Die Serialisierung und Deserialisierung dieser Parameter von der URL zu Router5 und umgekehrt sieht nicht übernatürlich aus, ist es aber.


 export default { name: 'help', path: '/help*slugs', loadComponent: () => import('./index'), encodeParams: ({ slugs }) => slugs.join('/'), decodeParams: ({ slugs }) => slugs.split('/'),, defaultParams: { slugs: [], }, }; 

Für unser Projekt war dies ein wichtiger Parameter für die Auswahl eines Routers, da unsere Anwendung viele Filter und Popups enthält. Und es sollte in der URL angezeigt werden, dass der Benutzer jetzt auf dem Bildschirm sieht. Wenn der Benutzer seinem Kollegen etwas zeigen möchte, fummelt er einfach an der URL in der Adressleiste seines Browsers herum.


Das Folgende sind grundlegende Beispiele:


 const router = router5([ { name: 'admin', path: '/admin' }, { name: 'admin.users', path: '~//users?param1' }, { name: 'admin.user', path: '^/user/:id' }, { name: 'help', path: '^/help/*splat' } ]); console.log(router.buildPath('admin')); // '/admin' console.log(router.buildPath('admin.users'); // '/users' console.log(router.buildPath('admin.users', { param1: true })); // '/users?param1=true' console.log(router.buildPath('admin.users', { id: 100 })); // '/user/100' console.log(router.buildPath('admin.user', { id: 100 })); // '/user/100' console.log(router.buildPath('help', { splat: [1, 2, 3] })); // '/help/1/2/3' 

In der React-Komponente erfolgt die Linkbildung wie folgt:


 import React from 'react' import { Link } from 'react-router5'function Menu(props) { return ( <nav> <Link routeName="home">Home</Link> <Link routeName="about">About</Link> <Link routeName="user" routeParams={{ id: 100 }}>User #100</Link> </nav> ) }​ export default Menu 

Beim Erstellen einfacher Sites, die aus 3-4 Seiten mit einer Mindestanzahl von Übergängen bestehen, verwende ich nicht Router5, sondern React-Router. Aber in Projekten mit vielen Seiten und Links werde ich mich für Router5 entscheiden.


Sie können sich hier auch ein Video meiner Leistung auf RamblerFront & # 5 ansehen:



PS In der ersten Oktoberhälfte 2018 planen wir die nächste RamblerFront & # 6. Folgen Sie der Ankündigung hier auf habr.com.

Source: https://habr.com/ru/post/de424025/


All Articles