Định tuyến máy khách (sử dụng bộ định tuyến phản ứng) và Định tuyến phía máy chủ


108

Tôi đã suy nghĩ và bối rối với việc định tuyến giữa Máy khách và Máy chủ. Giả sử tôi sử dụng ReactJS để hiển thị phía máy chủ trước khi gửi yêu cầu trở lại trình duyệt web và sử dụng bộ định tuyến phản ứng làm định tuyến phía máy khách để chuyển đổi giữa các trang mà không cần làm mới dưới dạng SPA.

Điều nghĩ đến là:

  • Các tuyến đường được giải thích như thế nào? Ví dụ: một yêu cầu từ Trang chủ ( /home) đến trang Bài đăng ( /posts)
  • Định tuyến đi đâu, ở phía máy chủ hay máy khách?
  • Làm sao nó biết nó được xử lý như thế nào?

1
Tôi khuyên bạn nên đọc API Lịch sử trong các trình duyệt.
WiredPrairie

Câu trả lời:


137

Lưu ý, câu trả lời này bao gồm phiên bản React Router 0.13.x - phiên bản 1.0 sắp tới có vẻ như nó sẽ có các chi tiết triển khai khác nhau đáng kể

Người phục vụ

Đây là điều tối thiểu server.jsvới bộ định tuyến phản ứng:

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

Nơi routesmô-đun xuất danh sách các tuyến:

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

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

Mỗi khi yêu cầu được gửi đến máy chủ, bạn tạo một phiên bản sử dụng một lần được Routerđịnh cấu hình với URL đến làm vị trí tĩnh của nó, được giải quyết dựa trên cây các tuyến để thiết lập các tuyến phù hợp phù hợp, gọi lại với cấp cao nhất trình xử lý tuyến đường sẽ được hiển thị và bản ghi các tuyến đường con phù hợp ở mỗi cấp độ. Đây là những gì được tham khảo khi bạn sử dụng <RouteHandler>thành phần trong một thành phần xử lý tuyến đường để hiển thị một tuyến đường con đã được khớp.

Nếu người dùng đã tắt JavaScript hoặc tải chậm, bất kỳ liên kết nào mà họ nhấp vào sẽ lại truy cập vào máy chủ, điều này sẽ được giải quyết lại như trên.

Khách hàng

Điều này là tối thiểu client.jsvới bộ định tuyến phản ứng (sử dụng lại mô-đun các tuyến đường giống nhau):

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

Khi bạn gọi Router.run(), nó tạo ra một phiên bản Router cho bạn ở hậu trường, được sử dụng lại mỗi khi bạn điều hướng xung quanh ứng dụng, vì URL có thể động trên máy khách, trái ngược với trên máy chủ nơi một yêu cầu duy nhất có URL cố định.

Trong trường hợp này, chúng tôi đang sử dụng HistoryLocation, sử dụng HistoryAPI để đảm bảo điều đúng đắn sẽ xảy ra khi bạn nhấn nút quay lại / chuyển tiếp. Ngoài ra còn có một HashLocationthay đổi URL hashđể tạo các mục lịch sử và lắng nghe window.onhashchangesự kiện để kích hoạt điều hướng.

Khi bạn sử dụng <Link>thành phần của react-router , bạn cung cấp cho nó một chỗ todựa là tên của một tuyến, cộng với bất kỳ paramsquerydữ liệu nào mà tuyến đó cần. Kết xuất <a>bởi thành phần này có một onClicktrình xử lý cuối cùng sẽ gọi router.transitionTo()đến cá thể bộ định tuyến với các đạo cụ mà bạn đã cung cấp liên kết, trông giống như sau:

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

Đối với một liên kết thông thường, liên kết này cuối cùng sẽ gọi location.push()đến bất kỳ loại Vị trí nào bạn đang sử dụng, xử lý các chi tiết về thiết lập lịch sử để điều hướng bằng các nút quay lại và chuyển tiếp sẽ hoạt động, sau đó gọi lại router.handleLocationChange()để cho bộ định tuyến biết rằng nó có thể tiến hành chuyển đổi sang đường dẫn URL mới.

Sau đó, bộ định tuyến gọi router.dispatch()phương thức của riêng nó với URL mới, phương thức này xử lý các chi tiết về việc xác định tuyến nào trong số các tuyến được cấu hình phù hợp với URL, sau đó gọi bất kỳ móc chuyển tiếp nào có mặt cho các tuyến đã khớp. Bạn có thể triển khai các móc chuyển tiếp này trên bất kỳ trình xử lý tuyến đường nào của mình để thực hiện một số hành động khi một tuyến đường sắp được điều hướng khỏi hoặc điều hướng đến, với khả năng hủy quá trình chuyển đổi nếu mọi thứ không theo ý muốn của bạn.

Nếu quá trình chuyển đổi không bị hủy bỏ, bước cuối cùng là gọi lệnh gọi lại mà bạn đã cung cấp Router.run()với thành phần trình xử lý cấp cao nhất và một đối tượng trạng thái với tất cả các chi tiết của URL và các tuyến đường phù hợp. Thành phần trình xử lý cấp cao nhất thực sự là Routerbản thân cá thể, xử lý việc hiển thị trình xử lý tuyến đường cao nhất đã được so khớp.

Quá trình trên được chạy lại mỗi khi bạn điều hướng đến một URL mới trên máy khách.

Dự án mẫu


3
Vì vậy, tôi có thể nói rằng định tuyến máy khách được xử lý bởi javascript (là mã bộ định tuyến phản ứng) nếu nó xuất hiện. Bất cứ khi nào tôi nhấn enter trên thanh địa chỉ của trình duyệt hoặc làm mới trang hoặc tắt JS, phía máy chủ sẽ xử lý việc định tuyến. Mặt khác, khi javascript đã sẵn sàng trên trang hiện tại, việc định tuyến sẽ được xử lý bởi phía máy khách. Tôi đã hiểu đúng chưa?
heartmon

9
Những gì trong mô-đun các tuyến đường var routes = require('./routes')Nó là một danh sách các tuyến đường? Tôi đã sử dụng tốc router nhưng ví dụ này đây trên SO có vẻ là ví dụ duy nhất về việc thiết lập máy chủ bên vẽ với Phản ứng Router, vì vậy nó sẽ là tốt nếu nó là một ví dụ mã đầy đủ
svnm

2
Nó phải là một danh sách các tuyến đường. Tôi sẽ thêm một ghi chú về điều đó và một số liên kết đến các dự án mẫu.
Jonny Buchanan

2
Vì vậy, nếu react-router chăm sóc định tuyến phía máy chủ thì ai sẽ nói chuyện với cơ sở dữ liệu? điều gì xảy ra với định tuyến phía máy chủ? hãy tưởng tượng chúng tôi muốn cung cấp API REST cho một ứng dụng di động gốc. Ai lo việc đó?
Morteza Shahriari Nia

1
Câu trả lời là nó đã lỗi thời do react-routerphiên bản mới hơn . Hãy cập nhật nó.
oleh.meleshko

26

Với 1.0, React-Router phụ thuộc vào mô-đun lịch sử như một peerDependency. Mô-đun này giải quyết vấn đề định tuyến trong trình duyệt. Theo mặc định, React-Router sử dụng API Lịch sử HTML5 ( pushState, replaceState), nhưng bạn có thể định cấu hình nó để sử dụng định tuyến dựa trên băm (xem bên dưới)

Việc xử lý tuyến đường hiện được thực hiện ở hậu trường và ReactRouter sẽ gửi các đạo cụ mới tới các trình xử lý tuyến đường khi tuyến đường thay đổi. Bộ định tuyến có một lệnh onUpdategọi lại hỗ trợ mới bất cứ khi nào một tuyến đường thay đổi, hữu ích cho việc theo dõi số lần xem trang hoặc cập nhật <title>ví dụ.

Máy khách (định tuyến 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)

Máy khách (định tuyến dựa trên băm)

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)

Người phục vụ

Trên máy chủ, chúng ta có thể sử dụng ReactRouter.match, điều này được lấy từ hướng dẫn kết xuất máy chủ

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')
    }
  })
})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.