Routage universel pour les applications React

Si vous essayez de décrire en un mot en quoi consiste la fonction de routage sur le front-end des applications Web, vous pouvez conclure que chaque framework populaire a une idée complètement différente de cela. Même en comparant les versions du même framework, nous pouvons conclure que les fonctions et les API de routage sont les plus sensibles aux changements (souvent sans compatibilité descendante). Par exemple, la 4e version du routage dans React a été repensée si radicalement que certains projets populaires sur githab.com ne sont pas passés à cette version.

Derrière tout cela, il y a une tendance générale qui, à mon avis, est que la fonctionnalité de routage dans de nombreux frameworks frontaux populaires est surchargée. À cet égard, il devient étroitement lié à d'autres composants qui pourraient être isolés du routage (par exemple, la navigation, l'historique, les liens, etc.). Par conséquent, probablement, beaucoup connaissent le sentiment lorsque l'utilisation du routage devient inconfortable et que son expansion est tout simplement impossible. Par rapport aux composants flexibles et extensibles, le routage dans les frameworks frontaux populaires semble beaucoup moins pratique et pas extensible du tout. Cela est particulièrement vrai des premières versions (jusqu'à la 4e) de routage dans React.

Dans cet article, j'examinerai certains points historiques qui ont conduit à cet état de choses avec le routage, ainsi que l'utilisation de la bibliothèque de routeurs universels, avec React.

Le routage est-il nécessaire?


Techniquement, une application Web d'une page peut fonctionner sans routage. Par exemple, car il n'y a pas de routage dans l'application de bureau. Tout fonctionnerait presque très bien si l'application Web d'une page ne restait pas la même application de navigateur Web. Autrement dit, l'utilisateur peut actualiser la page à tout moment en appuyant sur la touche F5 ou en cliquant sur l'icône «Recharger» du navigateur. Alternativement, l'utilisateur peut faire défiler l'histoire vers l'avant ou vers l'arrière à tout moment en cliquant sur les icônes «Flèche gauche» et «Flèche droite», ou en appuyant sur la touche «Retour arrière».

Par conséquent, pour une application d'une seule page, un changement de composants et un changement de l'état interne de l'application doivent toujours être accompagnés d'un changement d'URL.

Pourquoi le routage l'est-il?


La fonction de routage dans les frameworks populaires pour les applications Web frontales, à mon avis, est affectée par sa connexion historique avec le routage dans les applications Web classiques (avec rendu de serveur).

Initialement, l'URL était l'adresse Web d'un document Web statique, et c'était très simple. Ensuite, l'adaptation de l'architecture MVC au Web a commencé: le modèle 1 et le modèle 2. Le dernier d'entre eux comprend un contrôleur frontal, qui a ensuite été divisé en deux parties: le routage (qui sélectionne le contrôleur souhaité) et le contrôleur lui-même, qui fonctionne avec le modèle et vue de rendu. Comme vous pouvez le voir, dans une application Web classique, le routage détermine l'action (contrôleur) et, indirectement (via le contrôleur), détermine la vue qui doit être rendue sur le serveur.

Autrement dit, l'architecture de bureau a été adaptée à un moment donné pour fonctionner avec l'application Web classique sur le serveur, puis renvoyée à l'avant de l'application Web sous la forme d'un routage, qui était pondéré avec les fonctions nécessaires du côté serveur.

Qu'offre la bibliothèque de routeurs universels?


La bibliothèque de routeur universel propose de supprimer tout ce qui est superflu et de ne laisser que la partie qui peut être utilisée avec ou sans cadre lors du rendu à la fois sur le client et sur le côté du serveur Web (dans les applications Web universelles / isomorphes).

Ayant rejeté toutes les strates de temps, le routeur universel n'offre qu'une seule fonction clairement définie. Sur la base de la ligne (je souligne à nouveau la ligne et non l'objet historique, l'emplacement, etc.), appelez la fonction asynchrone, qui passera la chaîne d'URL analysée en tant que paramètres réels. C’est tout. À quoi cela pourrait ressembler dans React:

import React from 'react'; import UniversalRouter from 'universal-router'; import App from './App'; import Link from './Link'; const routes = { path: '/', async action({next}) { const children = await next(); return ( <App> {children} </App> ); }, children: [ { path: '', async action() { return ( <div>Root route go to <Link href='/test'>Test</Link></div> ); }, }, { path: '/test', async action({next}) { const children = await next(); return ( <App> {children} </App> ); }, children: [ { path: '', async action() { return ( <div>Test route return to <Link href='/'>Root</Link></div> ); }, }, ] }, ], }; export const basename = ''; const router = new UniversalRouter(routes, { baseUrl: basename }); export default router; 

Les itinéraires imbriqués sont également pris en charge. Ils sont définis dans le champ enfants, et vous pouvez les obtenir en appelant la fonction asynchrone next ().

Et comment cela fonctionne-t-il avec React?


Définissez la méthode naviguer () pour l'historique, bien que dans de nombreux cas il soit suffisant d'utiliser la méthode native push ()

 import { createBrowserHistory } from 'history' import parse from 'url-parse' import deepEqual from 'deep-equal' const isNode = new Function('try {return this===global;}catch(e){return false;}') //eslint-disable-line let history if (!isNode()) { history = createBrowserHistory() history.navigate = function (path, state) { const parsedPath = parse(path) const location = history.location if (parsedPath.pathname === location.pathname && parsedPath.query === location.search && parsedPath.hash === location.hash && deepEqual(state, location.state)) { return } const args = Array.from(arguments) args.splice(0, 2) return history.push(...[path, state, ...args]) } } else { history = {} history.navigate = function () {} } export default history 

Nous créons également le composant Link qui déclenchera la navigation:

 import React from 'react'; import {basename} from './router'; import history from './history'; function noOp(){}; const createOnClickAnchor = (callback) => { return (e) => { e.preventDefault(); history.navigate(e.currentTarget.getAttribute('href')); callback(e); }; }; export default ({href, onClick = noOp, children, ...rest}) => ( <a href={basename + href} onClick={createOnClickAnchor(onClick)} {...rest} > {children} </a> ); 

Vous êtes maintenant prêt à rendre le composant:

 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import history from './history'; import router from './router'; const render = async (location) => { const element = await router.resolve(location); ReactDOM.render( element, document.getElementById('root'), ); }; render(history.location); history.listen(render); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister(); 

Code de projet

Liens utiles

1. medium.com/@ippei.tanaka/universal-router-history-react-97ec79464573

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


All Articles