Маршрутизация клиента (с использованием react-router) и маршрутизация на стороне сервера

Я думал, и я смущен маршрутизацией между клиентом и сервером. Предположим, я использую ReactJS для рендеринга на стороне сервера перед отправкой запроса обратно в веб-браузер и использую react-router в качестве маршрутизации на стороне клиента для переключения между страницами без обновления как SPA.

что приходит на ум, это:

  • как интерпретируются маршруты? Например, запрос с домашней страницы (/home) на страницу сообщений (/posts)
  • причем тут маршрутизация перейти на серверную или клиентскую сторону?
  • как он знает, как он обрабатывается?

2 ответов


Примечание, этот ответ охватывает React Router версии 0.13.х -следующей версии 1.0 похоже, что он будет иметь значительно разные детали реализации

сервер

это минимальный server.js С 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})
  })
})

здесь routes модуль экспортирует список маршрутов:

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

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

каждый раз, когда запрос сделан на сервер, вы создаете одноразовыйRouter экземпляр с входящим URL-адресом в качестве его статического местоположения, который разрешен против дерева маршрутов, чтобы настроить соответствующие сопоставленные маршруты, перезванивая с обработчиком маршрута верхнего уровня, который будет отображаться, и запись о том, какие дочерние маршруты совпадают на каждом уровне. Это то, что консультируется, когда вы используете <RouteHandler> компонент в компоненте обработки маршрута для отображения дочернего маршрута, который был сопоставлен.

если у пользователя отключен JavaScript или он медленно загружается, любые ссылки, которые они нажимают on снова ударит по серверу, который будет разрешен снова, как указано выше.

клиент

это минимальный client.js С react-router (повторно используя тот же модуль маршрутов):

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

когда вы называете Router.run(), он создает экземпляр маршрутизатора для вас за кулисами, который повторно используется каждый раз, когда вы перемещаетесь по приложению, поскольку URL-адрес может быть динамическим на клиенте, в отличие от сервера, где один запрос имеет фиксированный URL-адрес.

в этом случае, мы используем HistoryLocation, который использует History API чтобы убедиться, что правильная вещь происходит, когда вы нажмете кнопку Назад / Вперед. Есть также HashLocation который изменяет URL hash, чтобы сделать записи истории и слушает window.onhashchange событие для запуска навигации.

при использовании react-router's <Link> компонент, вы даете ему to опора, которая является названием маршрута, плюс любой params и query данные, необходимые для маршрута. Этот <a> визуализация этого компонента имеет onClick обработчик, который в конечном счете называет router.transitionTo() на экземпляре маршрутизатора с реквизитом вы дали ссылку, которая выглядит так:

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

для регулярной ссылки это в конечном итоге вызывает location.push() на любом типе местоположения, который вы используете, который обрабатывает детали настройки истории, поэтому навигация с помощью кнопок назад и вперед будет работать, а затем возвращается к router.handleLocationChange() чтобы маршрутизатор знал, что он может перейти к новый URL-адрес.

маршрутизатор затем вызывает свой собственный router.dispatch() метод с новым URL, который обрабатывает детали определения, какой из настроенных маршрутов соответствует URL, а затем вызывает любой переход крючки присутствует для совпадающих маршрутов. Вы можете реализовать эти переходные крючки на любом из ваших обработчиков маршрута, чтобы предпринять некоторые действия, когда маршрут собирается перейти от или перейти к, с возможностью прервать переход, если вещи не к вашему симпатия.

если переход не был прерван, последним шагом является вызов обратного вызова, который вы дали Router.run() С компонентом обработчика верхнего уровня и объектом состояния со всеми деталями URL и сопоставленных маршрутов. Компонент обработчика верхнего уровня на самом деле является Router сам экземпляр, который обрабатывает рендеринг самого верхнего обработчика маршрута, который был сопоставлен.

вышеуказанный процесс повторяется каждый раз, когда вы переходите к новому URL-адресу на клиент.

примеры проектов


С 1.0, React-маршрутизатор зависит от история модуль как peerDependency. Этот модуль имеет дело с маршрутизацией в браузере. По умолчанию React-Router использует API истории HTML5 (pushState, replaceState), но вы можете настроить его для использования маршрутизации на основе хэша (см. ниже)

обработка маршрута теперь выполняется за кулисами, и ReactRouter отправляет новые реквизиты обработчикам маршрута при изменении маршрута. Маршрутизатор имеет новый onUpdate prop обратный вызов всякий раз, когда маршрут изменения, полезные для отслеживания просмотра страниц или обновления <title>, например.

клиент (маршрутизация 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)

клиент (маршрутизация на основе хэша)

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)

сервер

на сервере, мы можем использовать ReactRouter.match, это взято из руководство по рендерингу сервера

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