Universelles Routing für reaktive Anwendungen

Wenn Sie versuchen, auf den Punkt zu bringen, woraus die Routing-Funktion im Front-End von Webanwendungen besteht, können Sie zu dem Schluss kommen, dass jedes gängige Framework eine völlig andere Vorstellung davon hat. Selbst wenn wir die Versionen desselben Frameworks vergleichen, können wir den Schluss ziehen, dass die Funktionen und Routing-APIs am anfälligsten für Änderungen sind (häufig ohne Abwärtskompatibilität). Beispielsweise wurde die vierte Version von Reacts Routing so grundlegend überarbeitet, dass einige beliebte Projekte auf githab.com nicht auf diese Version umgestellt wurden.

Dahinter steckt ein allgemeiner Trend, der meiner Meinung nach darin besteht, dass die Routing-Funktionalität in vielen gängigen Front-End-Frameworks überlastet ist. In dieser Hinsicht wird es eng mit anderen Komponenten verbunden, die vom Routing isoliert sein können (z. B. Navigation, Verlauf, Links usw.). Daher sind wahrscheinlich viele mit dem Gefühl vertraut, wenn die Verwendung des Routings unangenehm wird und seine Erweiterung einfach unmöglich ist. Im Vergleich zu flexiblen und erweiterbaren Komponenten ist das Routing in gängigen Front-End-Frameworks weniger komfortabel und überhaupt nicht erweiterbar. Dies gilt insbesondere für die ersten Versionen (bis zum 4.) des Routings in React.

In diesem Beitrag werde ich auf einige historische Punkte eingehen, die zu diesem Sachverhalt beim Routing geführt haben, sowie auf die Verwendung der Universal-Router-Bibliothek zusammen mit React.

Ist ein Routing notwendig?


Technisch kann eine einseitige Webanwendung ohne Weiterleitung funktionieren. Zum Beispiel, weil in der Desktop-Anwendung kein Routing vorhanden ist. Alles würde fast gut funktionieren, wenn die einseitige Webanwendung nicht dieselbe Webbrowseranwendung wäre. Das heißt, der Benutzer kann die Seite jederzeit aktualisieren, indem er die Taste F5 drückt oder auf das Browser-Symbol "Neu laden" klickt. Oder der Benutzer kann die Story jederzeit vorwärts oder rückwärts scrollen, indem er auf die Symbole „Pfeil nach links“ und „Pfeil nach rechts“ klickt oder die Rücktaste drückt.

Daher sollte bei einer einseitigen Anwendung eine Änderung der Komponenten und eine Änderung des internen Status der Anwendung immer mit einer Änderung der URL einhergehen.

Warum ist Routing so?


Die Routing-Funktion in gängigen Frameworks für Front-End-Webanwendungen wird meiner Meinung nach durch die historische Verbindung mit dem Routing in klassischen Webanwendungen (mit Server-Rendering) beeinflusst.

Url war anfangs die Webadresse eines statischen Webdokuments und es war sehr einfach. Als nächstes begann die Anpassung der MVC-Architektur an das Web: Modell 1 und Modell 2. Die letzte umfasst einen Front-Controller, der später in zwei Teile unterteilt wurde: Routing (der den gewünschten Controller auswählt) und den Controller selbst, der mit dem Modell und zusammenarbeitet Ansicht rendern. Wie Sie sehen, bestimmt in einer klassischen Webanwendung das Routing die Aktion (Controller) und indirekt (über den Controller) die Ansicht, die auf dem Server gerendert werden soll.

Das heißt, die Desktop-Architektur wurde zu einem bestimmten Zeitpunkt an die klassische Webanwendung auf dem Server angepasst und dann in Form eines Routings an die Vorderseite der Webanwendung zurückgegeben, das mit Funktionen gewichtet wurde, die auf der Serverseite benötigt wurden.

Was bietet die Universal-Router-Bibliothek?


Die Universal-Router-Bibliothek bietet die Möglichkeit, alles Überflüssige zu verwerfen und nur den Teil zu belassen, der mit oder ohne Framework beim Rendern sowohl auf dem Client als auch auf der Seite des Webservers (in universellen / isomorphen Webanwendungen) verwendet werden kann.

Nachdem der Universal-Router alle Zeitschichten verworfen hat, bietet er nur eine klar definierte Funktion. Rufen Sie auf der Grundlage der Zeile (ich betone die Zeile erneut und nicht das Objekt der Geschichte, des Standorts usw.) die asynchrone Funktion auf, an die die analysierte URL-Zeichenfolge als tatsächliche Parameter gesendet wird. Das ist alles. So könnte es in React aussehen:

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; 

Verschachtelte Routen werden ebenfalls unterstützt. Sie werden im untergeordneten Feld definiert und können durch Aufrufen der asynchronen Funktion next () abgerufen werden.

Und wie funktioniert das mit React?


Definieren Sie die navigate () -Methode für den Verlauf, obwohl es in vielen Fällen ausreichend ist, die native push () -Methode zu verwenden

 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 

Wir erstellen auch die Link-Komponente, die die Navigation auslöst:

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

Jetzt können Sie die Komponente rendern:

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

Projektcode

Nützliche Links

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

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


All Articles