簡體   English   中英

客戶端路由(使用react-router)和服務器端路由

[英]Client Routing (using react-router) and Server-Side Routing

我一直在想,我對客戶端和服務器之間的路由感到困惑。 假設我在將請求發送回Web瀏覽器之前使用ReactJS進行服務器端呈現,並使用react-router作為客戶端路由在頁面之間切換而不刷新為SPA。

我想到的是:

  • 如何解釋路線? 例如,從主頁( /home )到帖子頁面( /posts )的請求
  • 路由在服務器端或客戶端上的位置是什么?
  • 它是如何知道如何處理的?

注意,這個答案涵蓋了React Router版本0.13.x - 即將推出的1.0版本看起來會有明顯不同的實現細節

服務器

這是帶有react-router的最小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,或者加載速度很慢,則他們點擊的任何鏈接都會再次點擊服務器,如上所述再次解決。

客戶

這是一個帶有react-router的最小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支撐這是一個路由的名稱,加上任何paramsquery數據的路由需求。 這個組件呈現的<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路徑。

然后,路由器使用新URL調用自己的router.dispatch()方法,該URL處理確定哪些配置的路由與URL匹配的詳細信息,然后調用匹配路由的任何轉換掛鈎 您可以在任何路由處理程序上實現這些轉換掛鈎,以便在路由即將導航或導航到路徑時執行某些操作,並且可以根據您的喜好中止轉換。

如果轉換沒有中止,最后一步是使用頂級處理程序組件調用您給Router.run()的回調,並調用包含URL和匹配路由的所有詳細信息的狀態對象。 頂級處理程序組件實際上是Router實例本身,它處理呈現匹配的最頂層路由處理程序。

每次導航到客戶端上的新URL時,都會重新運行上述過程。

示例項目

使用1.0,React-Router依賴於歷史模塊作為peerDependency。 該模塊處理瀏覽器中的路由。 默認情況下,React-Router使用HTML5 History API( pushStatereplaceState ),但您可以將其配置為使用基於散列的路由(請參閱下文)

路由處理現在在幕后完成,當路由更改時,ReactRouter將新的props發送到Route處理程序。 例如,每當路由發生變化時,路由器都會有一個新的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')
    }
  })
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM