web-dev-qa-db-fra.com

Routage client (à l'aide de react-routeur) et routage côté serveur

J'ai réfléchi et je suis confus avec le routage entre le client et le serveur. Supposons que j'utilise ReactJS pour le rendu côté serveur avant de renvoyer la demande au navigateur Web, et que react-router soit utilisé comme un routage côté client pour basculer entre les pages sans rafraîchissement en tant que SPA.

Ce qui me vient à l'esprit est:

  • Comment sont interprétées les routes? Par exemple, une demande de la page d'accueil (/home) vers la page des messages (/posts)
  • Où va le routage, côté serveur ou client?
  • Comment sait-il comment il est traité?
106
heartmon

Remarque: cette réponse couvre React La version de routeur 0.13.x - the version à venir 1. semble avoir des détails de mise en œuvre significativement différents

Serveur

Ceci est un server.js minimal avec 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})
  })
})

Où le module routes exporte une liste d'itinéraires:

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

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

Chaque fois qu'une demande est faite au serveur, vous créez une instance à usage unique Router configurée avec l'URL entrante comme emplacement statique. Cette instance est résolue par rapport à l'arborescence des itinéraires afin de configurer les itinéraires correspondants correspondants. avec le gestionnaire d’itinéraires de niveau supérieur à restituer et un enregistrement des itinéraires enfants correspondants à chaque niveau. C'est ce qui a été consulté lorsque vous utilisez le composant <RouteHandler> au sein d'un composant de gestion d'itinéraire pour rendre un itinéraire enfant correspondant.

Si l'utilisateur a désactivé JavaScript ou si son chargement est lent, tous les liens sur lesquels il cliquera frapperont à nouveau le serveur, ce qui sera résolu à nouveau comme ci-dessus.

Client

Il s'agit d'un client.js minimal avec react-router (ré-utilisant le même module de routage):

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

Lorsque vous appelez Router.run(), il crée une instance de routeur en coulisse, qui est réutilisée chaque fois que vous naviguez dans l'application, car l'URL peut être dynamique sur le client, par opposition au serveur sur lequel une seule demande a une URL fixe.

Dans ce cas, nous utilisons le HistoryLocation, qui utilise le History API pour vous assurer que la bonne chose se produit lorsque vous appuyez sur le bouton Précédent/Suivant. Il existe également un HashLocation qui modifie l'URL hash pour créer des entrées dans l'historique et écoute l'événement window.onhashchange pour déclencher la navigation.

Lorsque vous utilisez le composant <Link> de react-router, vous lui donnez un objet to qui est le nom d'un itinéraire, plus les données params et query dont l'itinéraire a besoin. Le <a> rendu par ce composant a un gestionnaire onClick qui appelle finalement router.transitionTo() sur l'instance du routeur avec les accessoires que vous avez fournis avec le lien, qui ressemble à ceci:

  /**
   * 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);
    }
  },

Pour un lien normal, cela appelle finalement location.Push() sur le type d'emplacement que vous utilisez, qui gère les détails de la configuration de l'historique pour que la navigation avec les boutons Précédent et Suivant fonctionne, puis appelle à nouveau router.handleLocationChange() pour informez le routeur qu'il peut poursuivre la transition vers le nouveau chemin d'URL.

Le routeur appelle ensuite sa propre méthode router.dispatch() avec la nouvelle URL, qui gère les détails de la détermination des routes configurées correspondant à l'URL, puis appelle any crochets de transition présent pour les routes correspondantes. Vous pouvez implémenter ces crochets de transition sur n’importe quel de vos gestionnaires d’itinéraires afin de prendre des mesures lorsqu'un itinéraire est sur le point de quitter ou de naviguer, avec la possibilité d’interrompre la transition si les choses ne vous conviennent pas.

Si la transition n'a pas été annulée, l'étape finale consiste à appeler le rappel que vous avez donné à Router.run() avec le composant de gestionnaire de niveau supérieur et un objet state avec tous les détails de l'URL et des itinéraires correspondants. Le composant de gestionnaire de niveau supérieur est en fait l'instance Router, elle-même, qui gère le rendu du gestionnaire de routage le plus élevé qui a été mis en correspondance.

Le processus ci-dessus est réexécuté chaque fois que vous accédez à une nouvelle URL sur le client.

Exemples de projets

135
Jonny Buchanan

Avec 1.0, React-Router dépend du module historique en tant que peerDependency. Ce module traite du routage dans le navigateur. Par défaut, React-Router utilise l'API Historique HTML5 (pushState, replaceState), mais vous pouvez le configurer pour utiliser un routage basé sur un hachage (voir ci-dessous).

La gestion de la route est maintenant effectuée dans les coulisses et ReactRouter envoie de nouveaux accessoires aux gestionnaires Route lors du changement de route. Le routeur dispose d'un nouveau rappel onUpdateprop à chaque changement de route, ce qui est utile pour le suivi de la consultation des pages ou la mise à jour du <title>, par exemple.

Client (routage HTML5)

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 (routage basé sur un hachage)

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)

Serveur

Sur le serveur, nous pouvons utiliser ReactRouter.match, ceci est tiré du guide de rendu du serveur

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