Enrutamiento en una gran aplicación React


Hola, mi nombre es Boris Shabanov , soy el jefe de desarrollo de Frontend en el departamento de desarrollo de tecnologías publicitarias del Grupo Rambler. Hoy les contaré cómo surgieron los problemas de enrutamiento en nuestra aplicación y cómo los resolvimos.


Advertising Technologies es un gran departamento en el Grupo Rambler que implementa una pila completa de tecnologías publicitarias (SSP, DMP, DSP). Para los usuarios finales, es decir, los anunciantes y las agencias, estamos creando una interfaz conveniente llamada Summer , en la que pueden lanzar sus campañas publicitarias, ver sus estadísticas y administrar todo.



La interfaz de verano es un conjunto de pantallas, formas conectadas por enlaces entre sí. Al estar en un lugar, en unos pocos clics, el usuario puede encontrar la información que necesita.



Después de varios años de operación, los requisitos del proyecto se volvieron más serios y nos dimos cuenta de que necesitábamos solucionarlo. Dado que el proyecto se basó en React desde el principio, tomamos la aplicación React Create como base para la nueva versión. Y GraphQL , para la interacción con el servidor, a saber, el cliente Apollo . Además, el departamento tiene mucha experiencia.


Pero teníamos una pregunta, y ¿qué se puede usar para enrutar nuestra aplicación? Fuimos a Google para buscar una solución con las palabras "reaccionar" y "enrutador" y, de hecho, en las primeras 5 páginas no encontramos nada más que un enrutador React . "Bueno, está bien ..." - pensamos, la gente inteligente recomienda, debemos tomar. Después de un corto período de preguntas no se volvió menos. Por ejemplo, tome un ejemplo simple de usar 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> ); 

Surgen preguntas:


  • ¿Qué pasa con una URL con muchos parámetros?
  • ¿Qué debo hacer si llega el gerente de producto y me pide que cambie la URL de la página a una más amigable? "Compromiso de la alfombra" en todo el proyecto realmente no quiere hacer.

Durante el proceso de desarrollo, comenzamos a pensar y buscar soluciones, pero al mismo tiempo todavía vivíamos en React-router .


En el siguiente sprint, tomamos la sección "Ayuda" para trabajar. No es una página complicada, y en unas pocas horas lo hicimos.


Pero el punto es que la "Ayuda" es una sección a la que todas las otras páginas enlazan, y hay muchos enlaces a ella. Y el enlace a cada respuesta se genera dinámicamente. Para nosotros, esta fue otra razón para pensar en cambiar el enrutador.


Después de extensas búsquedas en Internet, encontramos una solución aceptable: Router5 .



Router5 es una biblioteca que no tiene nada que ver con React, puedes usarla con Angular, con jQuery, con cualquier cosa. Vale la pena decir de inmediato que Router5 no es una continuación de React-router @ 4, son dos cosas diferentes, desarrolladas por diferentes personas, y no están conectadas de ninguna manera.


En consecuencia, al elegir una biblioteca, comenzamos a ver qué tan popular es y si tiene sentido usarla. Si compara por el número de estrellas en github, entonces la ventaja es hacia el enrutador de reacción.



Pero decidimos arriesgarnos. Observamos cómo vive el proyecto, si alguien está involucrado en su desarrollo, vimos que las personas le están aportando cosas nuevas y el proyecto está muy vivo.



De la documentación puede descubrir que tiene un montón de integraciones con diferentes marcos y bibliotecas. En particular, la documentación describe la integración con react y redux, pero si busca bien, puede encontrar ejemplos con mobX allí.



¿Cómo se construyen las interfaces y cómo se construyen los enrutadores en Router5?
La historia es la misma aquí que en React-router: cada ruta es un contenedor para sus hijos.


Supongamos que tiene un sitio que consta de una página de destino (lo tendremos en casa), y hay un cierto panel de administración que consta de dos secciones: una lista de usuarios y una lista de grupos de estos usuarios. Podemos configurar aún más todo esto de tal manera que nuestra sección de Inicio se vea como una página de destino. Además, la sección de administración tendrá un contenedor para todas sus páginas y elementos secundarios, es decir, consistirá en un pie de página y un encabezado, por ejemplo. Todas las subpáginas que ya están en el área de administración se convertirán en este pie de página y encabezado.



Para dicho sitio, la configuración del Router5 será la siguiente:


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

En el ejemplo, tenemos una opción simple, pero de hecho puede aprender mucho más de la documentación y elegir un formato más conveniente para usted.


Como dije anteriormente, usamos React en nuestros proyectos en la división, por lo que continuaré hablando y mostrando ejemplos más hacia React. El siguiente código de ejemplo muestra cómo generar un proyecto usando 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) 

Además más interesante. Lo que nos gustó y encajamos muy elegantemente en el proyecto es cómo se hacen las transiciones. Digamos que tenemos un sitio con la estructura descrita anteriormente. Y nuestro usuario quiere ir de la página de inicio a la sección "Lista de usuarios". ¿Qué hará el router5 en este caso? En primer lugar, desactiva la sección de inicio, provocando los eventos correspondientes. Los eventos se pueden procesar tanto en la ruta como en el middleware, donde se pueden procesar todas estas transiciones. A continuación, se generan eventos de activación para las rutas admin y admin.users.



En nuestra aplicación, todo el middleware se recopila en un solo lugar. Estos son middleware, que son responsables de cargar los componentes (tratamos de "cortar" la aplicación en partes y cargar las partes que el usuario necesita ahora), localización, obtención de datos del servidor, verificación de derechos de acceso y recopilación de análisis de transición. Como resultado, los desarrolladores ni siquiera piensan en cómo recibir datos, verificar permisos y qué mostrar en la pantalla. Hacen la siguiente sección, y Router5 resuelve todos los problemas.


Otro problema que queríamos resolver de una vez por todas es cargar la aplicación, dividida en diferentes partes. En mi opinión, descargar una aplicación con React-router es similar a las aventuras de Hedgehog en la niebla. Al cargar algunas piezas y datos en cada etapa, su aplicación no es inmune a los errores al recibir datos del servidor. Y en este caso, sus diseñadores deben determinar el estado de las pantallas para cada situación de emergencia. Parece que en este lugar la probabilidad de un error del desarrollador es alta (tal vez lo olvide) y estos riesgos deben minimizarse.


En el Router5, esto se decidió por el hecho de que cada transición se lleva a cabo transaccionalmente, y el cambio de la parte visual no ocurrirá hasta que se haya resuelto todo el middleware, y el enrutador esté convencido de que nuestra aplicación puede extraer lo que queremos de él. Este enfoque tiene sus desventajas y ventajas, pero en nuestro caso hubo más ventajas. El problema con el indicador de carga se resuelve de inmediato. Nuestra aplicación utiliza un indicador de descarga, que es controlado por el enrutador.


Desafortunadamente, trabajar con middleware hace unos 3 o 4 meses no se reveló completamente en la documentación. Pero profundizando en los problemas, encontramos un gran ejemplo que muestra cómo implementar y crear una aplicación usando React, Redux y Router5.


Cada una de las URL de nuestra aplicación almacena un cierto conjunto de datos que necesitamos para mostrar datos (identificadores, opciones de filtrado de datos adicionales, etc.). La serialización y deserialización de estos parámetros desde la URL al enrutador5 y viceversa no se ve sobrenatural, pero lo es.


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

Para nuestro proyecto, este fue un parámetro importante para elegir un enrutador, porque nuestra aplicación tiene muchos filtros y ventanas emergentes. Y debería aparecer en la URL que indica que el usuario ahora ve en la pantalla. Si el usuario quiere mostrarle algo a su colega, simplemente busca la URL de la barra de direcciones de su navegador.


Los siguientes son ejemplos básicos:


 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' 

En el componente Reaccionar, la formación de enlaces ocurre de la siguiente manera:


 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 

Al crear sitios simples que consisten en 3-4 páginas con un número mínimo de transiciones, no uso Router5, pero uso React-router. Pero en proyectos con muchas páginas y enlaces, mi elección será hacia Router5.


También puedes ver un video de mi actuación en RamblerFront & # 5 aquí:



PD: En la primera mitad de octubre de 2018, planeamos celebrar el próximo RamblerFront & # 6. Siga el anuncio aquí en habr.com.

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


All Articles