it-swarm.com.de

Client-Routing (mit React-Router) und serverseitiges Routing

Ich habe nachgedacht und bin verwirrt mit dem Routing zwischen Client und Server. Angenommen, ich verwende ReactJS für das serverseitige Rendern, bevor ich die Anforderung an den Webbrowser zurückschicke, und verwende React-Router als clientseitiges Routing, um zwischen Seiten zu wechseln, ohne sie als SPA zu aktualisieren.

Was mir einfällt, ist:

  • Wie werden die Routen interpretiert? Zum Beispiel eine Anfrage von der Homepage (/home) zu Beitragsseite (/posts)
  • Wohin geht das Routing, auf der Serverseite oder auf dem Client?
  • Woher weiß es, wie es verarbeitet wird?
106
heartmon

Beachten Sie, dass diese Antwort React Router Version 0.13.x - die kommende Version 1. scheint signifikant unterschiedliche Implementierungsdetails zu haben

Server

Dies ist ein minimaler server.js Mit React-Router:

var express = require('express')
var React = require('react')
var Router = require('react-router')

var routes = require('./routes')

var app = express()

// ...express config...

app.use(function(req, res, next) {
  var router = Router.create({location: req.url, routes: routes})
  router.run(function(Handler, state) {
    var html = React.renderToString(<Handler/>)
    return res.render('react_page', {html: html})
  })
})

Wobei das routes Modul eine Liste von Routen exportiert:

var React = require('react')
var {DefaultRoute, NotFoundRoute, Route} = require('react-router')

module.exports = [
  <Route path="/" handler={require('./components/App')}>
    {/* ... */}
  </Route>
]

Bei jeder Anforderung an den Server erstellen Sie eine einmal verwendbare Router -Instanz, die mit der eingehenden URL als statischem Speicherort konfiguriert ist. Diese wird anhand des Routenbaums aufgelöst, um die entsprechenden übereinstimmenden Routen für den Aufruf einzurichten zurück mit dem Routen-Handler der obersten Ebene, der gerendert werden soll, und einer Aufzeichnung, welche untergeordneten Routen auf jeder Ebene übereinstimmten. Dies wird konsultiert, wenn Sie die Komponente <RouteHandler> In einer Routenbehandlungskomponente verwenden, um eine übereinstimmende untergeordnete Route zu rendern.

Wenn der Benutzer JavaScript deaktiviert hat oder das Laden langsam ist, werden alle Links, auf die er klickt, erneut auf den Server übertragen. Dies wird wie oben beschrieben behoben.

Klient

Dies ist ein minimaler client.js Mit React-Router (Wiederverwendung des gleichen Routenmoduls):

var React = require('react')
var Router = require('react-router')

var routes = require('./routes')

Router.run(routes, Router.HistoryLocation, function(Handler, state) {
  React.render(<Handler/>, document.body)
})

Wenn Sie Router.run() aufrufen, wird eine Router-Instanz für Sie hinter den Kulissen erstellt, die bei jedem Navigieren in der App erneut verwendet wird, da die URL auf dem Client dynamisch sein kann und nicht auf dem Server Wobei eine einzelne Anfrage eine feste URL hat.

In diesem Fall verwenden wir das HistoryLocation, das das History API verwendet, um sicherzustellen, dass das Richtige passiert, wenn Sie die Zurück-/Vorwärts-Taste drücken. Es gibt auch ein HashLocation, das die URL hash ändert, um Verlaufseinträge vorzunehmen, und das window.onhashchange -Ereignis abhört, um die Navigation auszulösen.

Wenn Sie die <Link> - Komponente des React-Routers verwenden, geben Sie ihr eine to -Stütze, die den Namen einer Route enthält, sowie alle params- und query -Daten Routenbedürfnisse. Das von dieser Komponente gerenderte <a> Hat einen onClick -Handler, der letztendlich router.transitionTo() auf der Router-Instanz mit den Requisiten aufruft, die Sie dem Link gegeben haben. Dies sieht folgendermaßen aus:

  /**
   * Transitions to the URL specified in the arguments by pushing
   * a new URL onto the history stack.
   */
  transitionTo: function (to, params, query) {
    var path = this.makePath(to, params, query);

    if (pendingTransition) {
      // Replace so pending location does not stay in history.
      location.replace(path);
    } else {
      location.Push(path);
    }
  },

Bei einem regulären Link wird letztendlich location.Push() für den von Ihnen verwendeten Standorttyp aufgerufen, wobei die Details zum Einrichten des Verlaufs behandelt werden, sodass das Navigieren mit den Schaltflächen "Zurück" und "Vorwärts" funktioniert. Anschließend wird erneut router.handleLocationChange() aufgerufen. ], um den Router darüber zu informieren, dass der Übergang zum neuen URL-Pfad fortgesetzt werden kann.

Der Router ruft dann seine eigene router.dispatch() -Methode mit der neuen URL auf, die die Details zum Ermitteln der mit der URL übereinstimmenden konfigurierten Routen verarbeitet, und ruft dann alle Übergangshooks auf, die für die übereinstimmenden Routen vorhanden sind . Sie können diese Übergangs-Hooks in jedem Ihrer Routen-Handler implementieren, um Maßnahmen zu ergreifen, wenn eine Route vor der Navigation steht. Sie können den Übergang auch abbrechen, wenn die Dinge nicht Ihren Wünschen entsprechen.

Wenn der Übergang nicht abgebrochen wurde, besteht der letzte Schritt darin, den von Ihnen an Router.run() übergebenen Rückruf mit der Handlerkomponente der obersten Ebene und einem Statusobjekt mit allen Details der URL und den übereinstimmenden Routen aufzurufen. Die Top-Level-Handler-Komponente ist eigentlich die Router -Instanz selbst, die das Rendern des Top-Level-Route-Handlers handhabt, zu dem eine Übereinstimmung gefunden wurde.

Der oben beschriebene Vorgang wird jedes Mal wiederholt, wenn Sie auf dem Client zu einer neuen URL navigieren.

Beispielprojekte

135
Jonny Buchanan

Mit 1.0 hängt React-Router vom history Modul als PeerDependency ab. Dieses Modul behandelt das Routing im Browser. Standardmäßig verwendet React-Router die HTML5-Verlaufs-API (pushState, replaceState), Sie können sie jedoch so konfigurieren, dass sie hashbasiertes Routing verwendet (siehe unten).

Das Routenhandling erfolgt jetzt hinter den Kulissen, und ReactRouter sendet neue Requisiten an die Routenhandler, wenn sich die Route ändert. Der Router verfügt über einen neuen onUpdate Requisitenrückruf, wenn sich eine Route ändert. Dies ist nützlich für die Verfolgung von Seitenaufrufen oder für die Aktualisierung der <title>, beispielsweise.

Client (HTML5-Routing)

import {Router} from 'react-router'
import routes from './routes'

var el = document.getElementById('root')

function track(){
  // ...
}

// routes can be children
render(<Router onUpdate={track}>{routes}</Router>, el)

Client (Hash-basiertes Routing)

import {Router} from 'react-router'
import {createHashHistory} from 'history'
import routes from './routes'

var el = document.getElementById('root')

var history = createHashHistory()

// or routes can be a prop
render(<Router routes={routes} history={history}></Router>, el)

Server

Auf dem Server können wir ReactRouter.match, dies ist aus dem Server Rendering Guide entnommen

import { renderToString } from 'react-dom/server'
import { match, RoutingContext } from 'react-router'
import routes from './routes'

app.get('*', function(req, res) {
  // Note that req.url here should be the full URL path from
  // the original request, including the query string.
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.status(500).send(error.message)
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
      res.status(200).send(renderToString(<RoutingContext {...renderProps} />))
    } else {
      res.status(404).send('Not found')
    }
  })
})
26
tom