Маршрутизация клиента (с использованием реакции-маршрутизатора) и маршрутизация на стороне сервера
Я думал, и меня смущает маршрутизация между клиентом и сервером. Предположим, я использую ReactJS для рендеринга на стороне сервера перед отправкой запроса обратно в веб-браузер, и использую реагирующий маршрутизатор в качестве маршрутизации на стороне клиента для переключения между страницами без обновления в качестве SPA.
Что приходит на ум это:
- Как интерпретируются маршруты? Например, запрос с домашней страницы (
/home
) к странице сообщений (/posts
) - Куда идет маршрутизация, на стороне сервера или на клиенте?
- Откуда оно знает, как оно обрабатывается?
2 ответа
Обратите внимание, что этот ответ охватывает React Router версии 0.13.x - следующая версия 1.0, похоже, будет существенно отличаться от деталей реализации.
сервер
Это минимальный server.js
с реакции-роутером:
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 или он загружается медленно, любые ссылки, по которым он щелкает, снова попадают на сервер, что снова разрешается, как указано выше.
клиент
Это минимальный client.js
с реакции-маршрутизатором (повторное использование того же модуля маршрутов):
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()
он создает экземпляр Router для вас за кулисами, который повторно используется каждый раз, когда вы перемещаетесь по приложению, поскольку URL-адрес может быть динамическим на клиенте, а не на сервере, где один запрос имеет фиксированный URL-адрес.
В этом случае мы используем HistoryLocation
, который использует History
API, чтобы убедиться, что правильно происходит, когда вы нажимаете кнопку назад / вперед. Там также есть HashLocation
который меняет URL hash
делать записи в истории и слушает window.onhashchange
событие для запуска навигации.
Когда вы используете ответ-роутер <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-Router зависит от модуля истории как peerDependency. Этот модуль занимается маршрутизацией в браузере. По умолчанию React-Router использует API истории HTML5 (pushState
, replaceState
), но вы можете настроить его для использования маршрутизации на основе хеша (см. ниже)
Обработка маршрута теперь выполняется за кулисами, и ReactRouter отправляет новые реквизиты в обработчики маршрутов при изменении маршрута. В роутере появился новый onUpdate
Пропуск обратного вызова при изменении маршрута, полезен для отслеживания просмотра страниц или обновления <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')
}
})
})