Routage dans une grande application React


Bonjour, je m'appelle Boris Shabanov , je suis responsable du développement Frontend au département de développement des technologies publicitaires du Groupe Rambler. Aujourd'hui, je vais vous expliquer comment des problèmes de routage sont survenus sur notre application et comment nous les avons résolus.


Advertising Technologies est un grand département du groupe Rambler qui met en œuvre une pile complète de technologies publicitaires (SSP, DMP, DSP). Pour les utilisateurs finaux, à savoir les annonceurs et les agences, nous créons une interface pratique appelée Summer , dans laquelle ils peuvent lancer leurs campagnes publicitaires, consulter des statistiques à leur sujet et tout gérer.



L'interface d' été est un ensemble d'écrans, de formes reliées entre elles par des liens. En un seul endroit, l'utilisateur peut trouver en quelques clics les informations dont il a besoin.



Après plusieurs années de fonctionnement, les exigences du projet sont devenues plus sérieuses et nous avons réalisé que nous devions y remédier. Comme le projet était basé sur React depuis le tout début, nous avons pris l'application React Create comme base pour la nouvelle version. Et GraphQL - pour l'interaction avec le serveur, à savoir - le client Apollo . De plus, le département possède une grande expertise.


Mais nous avions une question, et que peut-on utiliser pour acheminer notre application? Nous sommes allés sur Google pour chercher une solution avec les mots "réagir" et "routeur", et, en fait, sur les 5 premières pages, nous n'avons trouvé qu'un routeur React . "Eh bien, ok ..." - nous avons pensé, les gens intelligents recommandent, nous devons prendre. Après une courte période de questions n'est pas devenu moins. Par exemple, prenez un exemple simple d'utilisation de react-router:


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> ); 

Des questions se posent:


  • Et une URL avec beaucoup de paramètres?
  • Que dois-je faire si le chef de produit arrive et me demande de changer l'URL de la page en une URL plus «conviviale»? "Carpet commit" tout au long du projet n'a pas vraiment envie de faire.

Au cours du processus de développement, nous avons commencé à réfléchir et à chercher des solutions, mais en même temps, nous vivions encore sur React-router .


Au sprint suivant, nous avons pris la section "Aide" pour travailler. Pas une page compliquée, et en quelques heures nous l'avons fait.


Mais le fait est que "Aide" est une section à laquelle toutes les autres pages renvoient, et il y a beaucoup de liens vers elle. Et le lien vers chaque réponse est généré dynamiquement. Pour nous, c'était une autre raison de penser à changer de routeur.


Après de longues recherches sur Internet, nous avons trouvé une solution acceptable - Router5 .



Router5 est une bibliothèque qui n'a rien à voir avec React, vous pouvez l'utiliser avec Angular, avec jQuery, avec n'importe quoi. Cela vaut la peine de dire tout de suite que Router5 n'est pas une continuation de React-router @ 4, ce sont deux choses différentes, développées par des personnes différentes, et elles ne sont en aucun cas connectées.


En conséquence, lors du choix d'une bibliothèque, nous avons commencé à examiner sa popularité et s'il était judicieux de l'utiliser. Si vous comparez par le nombre d'étoiles sur github, alors l'avantage est vers react-router.



Mais nous avons décidé de tenter notre chance. Nous avons regardé comment le projet vit, si quelqu'un est engagé dans son développement, avons vu que les gens y apportent de nouvelles choses, et le projet est très vivant.



À partir de la documentation, vous pouvez découvrir qu'il a un tas d'intégrations avec différents cadres et bibliothèques. En particulier, la documentation décrit l'intégration avec react et redux, mais si vous recherchez bien, vous pouvez y trouver des exemples avec mobX.



Comment les interfaces sont-elles construites et comment les routeurs sont-ils construits dans Router5?
L'histoire est la même ici que dans React-router: chaque route est un conteneur pour ses enfants.


Supposons que vous ayez un site qui se compose d'une page de destination (nous l'aurons à la maison), et qu'il y ait un certain panneau d'administration qui se compose de deux sections - une liste d'utilisateurs et une liste de groupes de ces utilisateurs. Nous pouvons encore configurer tout cela de manière à ce que notre section d'accueil ressemble à une page de destination. De plus, la section admin aura un wrapper pour toutes ses pages et éléments enfants, c'est-à-dire sera composé d'un pied de page et d'un en-tête, par exemple. Toutes les sous-pages déjà dans la zone d'administration se transformeront en pied de page et en-tête.



Pour un tel site, la configuration de Router5 sera la suivante:


 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(); 

Dans l'exemple, nous avons une option simple, mais en fait, vous pouvez en apprendre beaucoup plus dans la documentation et choisir un format plus pratique pour vous-même.


Comme je l'ai dit plus tôt, nous utilisons React sur nos projets dans la division, je vais donc continuer à parler et à montrer des exemples plus à propos de React. L'exemple de code ci-dessous montre comment générer un projet à l'aide de Router @ 5.


 // 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) 

Plus intéressant encore. La chose que nous avons aimé et qui s'intègre très élégamment dans le projet est la façon dont les transitions sont effectuées. Disons que nous avons un site avec la structure décrite ci-dessus. Et notre utilisateur souhaite passer de la page de destination à la section "Liste des utilisateurs". Que fera router5 dans ce cas? Tout d'abord, il désactive la section d'accueil, provoquant les événements correspondants. Les événements peuvent être traités à la fois dans la route elle-même et dans le middleware, où toutes ces transitions peuvent être traitées. Ensuite, des événements d'activation pour les itinéraires admin et admin.users sont générés.



Dans notre application, tous les middlewares sont collectés en un seul endroit. Il s'agit du middleware, qui est responsable du chargement des composants (nous essayons de «couper» l'application en morceaux et de charger les pièces dont l'utilisateur a besoin maintenant), de la localisation, de l'obtention des données du serveur, de la vérification des droits d'accès et de la collecte des analyses de transition. En conséquence, les développeurs ne pensent même pas à la façon de recevoir des données, de vérifier les autorisations et de quoi afficher à l'écran. Ils font la section suivante et Router5 résout tous les problèmes.


Un autre problème que nous voulions résoudre une fois pour toutes est le chargement de l'application, divisée en différents morceaux. À mon avis, le téléchargement d'une application avec React-router est similaire aux aventures de Hedgehog dans le brouillard. En chargeant certains morceaux et données à chaque étape, votre application n'est pas à l'abri des erreurs de réception de données du serveur. Et dans ce cas, vos concepteurs doivent trouver le statut des écrans pour chaque situation d'urgence. Il semble qu'à cet endroit, la probabilité d'erreur d'un développeur soit élevée (il peut juste l'oublier), et ces risques devraient être minimisés.


Dans Router5, cela a été décidé par le fait que chaque transition est effectuée de manière transactionnelle, et le changement de la partie visuelle ne se produira que lorsque tous les middleware auront été élaborés, et le routeur est convaincu que notre application peut en tirer ce que nous voulons. Cette approche a ses inconvénients et ses avantages, mais dans notre cas, il y avait plus d'avantages. Le problème avec l'indicateur de chargement est immédiatement résolu. Notre application utilise un indicateur de téléchargement, contrôlé par le routeur.


Malheureusement, travailler avec un middleware il y a environ 3 ou 4 mois n'a pas été entièrement divulgué dans la documentation. Mais en explorant les problèmes, nous avons trouvé un excellent exemple montrant comment déployer et créer une application à l'aide de React, Redux et Router5.


Chacune des URL de notre application stocke un certain ensemble de données dont nous avons besoin pour afficher les données (identifiants, options de filtrage de données supplémentaires, etc.). La sérialisation et la désérialisation de ces paramètres de l'URL vers router5 et vice versa ne semblent pas surnaturelles, mais elles le sont.


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

Pour notre projet, il s'agissait d'un paramètre important pour le choix d'un routeur, car notre application dispose de nombreux filtres et pop-ups. Et il devrait s'afficher dans l'URL qui indique que l'utilisateur voit maintenant à l'écran. Si l'utilisateur veut montrer quelque chose à son collègue, il cherche simplement l'URL dans la barre d'adresse de son navigateur.


Voici des exemples de base:


 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' 

Dans le composant React, la formation de liens se produit comme suit:


 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 

Lors de la création de sites simples composés de 3-4 pages avec un nombre minimum de transitions, je n'utilise pas Router5, mais j'utilise React-router. Mais dans les projets avec de nombreuses pages et liens, mon choix se portera sur Router5.


Vous pouvez également regarder une vidéo de ma performance sur RamblerFront & # 5 ici:



PS Dans la première moitié d'octobre 2018, nous prévoyons d'organiser le prochain RamblerFront & # 6. Suivez l'annonce ici sur habr.com.

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


All Articles