Клієнтська маршрутизація (використовуючи маршрутизатор реакцій) та маршрутизація на стороні сервера


108

Я думав, і я плутаю маршрутизацію між клієнтом і сервером. Припустимо, я використовую ReactJS для візуалізації на стороні сервера перед відправкою запиту назад у веб-браузер і використовую react-router як маршрутизацію на стороні клієнта для перемикання між сторінками без оновлення як SPA.

Що спадає на думку:

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

1
Я б запропонував прочитати API API в браузерах.
WiredPrairie

Відповіді:


137

Зауважте, ця відповідь стосується 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(), він створює для вас екземпляр маршрутизатора поза кадром, який повторно використовується щоразу, коли ви переходите через додаток, оскільки URL-адреса може бути динамічною для клієнта, на відміну від сервера, де в одному запиті є фіксована URL-адреса.

У цьому випадку ми користуємось тим HistoryLocation, що використовує HistoryAPI, щоб переконатися, що правильне відбувається при натисканні на кнопку назад / вперед. Там же є 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-адреси клієнта.

Приклад проектів


3
Тож я, напевно, можу сказати, що клієнтська маршрутизація обробляється javascript (що є кодом реакції маршрутизатора), якщо він представлений. Щоразу, коли я натискаю клавішу Enter у адресному рядку веб-переглядача або оновлювати сторінку або відключати JS, сторона сервера обробляє маршрутизацію. З іншого боку, коли javascript буде готовий на поточній сторінці, маршрутизацію буде обробляти клієнтська сторона. Я правильно зрозумів?
серцевина

9
Що є в модулі маршрутів var routes = require('./routes')Це список маршрутів? Я використовував маршрутизатор Express, але цей приклад тут на SO здається єдиним прикладом налаштування візуалізації на стороні сервера за допомогою React Router, тому було б добре, якби це був повний приклад коду
svnm

2
Це повинен бути список маршрутів. Я додам примітку про це та деякі посилання на приклади проектів.
Джоні Бюкенан

2
Отже, якщо реактор-маршрутизатор піклується про маршрутизацію на стороні сервера, то хто спілкується з базою даних? що відбувається з маршрутизацією на стороні сервера? уявіть, що ми хочемо надати API REST для нативного мобільного додатка. Хто про це піклується?
Morteza Shahriari Nia

1
Відповідь застаріла через нову react-routerверсію. Будь ласка, оновіть його.
oleh.meleshko

26

З 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')
    }
  })
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.