客户端路由(使用react-router)和服务器端路由

hea*_*mon 106 javascript routes url-routing reactjs react-router

我一直在想,我对客户端和服务器之间的路由感到困惑.假设我在将请求发送回Web浏览器之前使用ReactJS进行服务器端呈现,并使用react-router作为客户端路由在页面之间切换而不刷新为SPA.

我想到的是:

  • 如何解释路线?例如,从主页(/home)到帖子页面(/posts)的请求
  • 路由在服务器端或客户端上的位置是什么?
  • 它是如何知道如何处理的?

Jon*_*nan 135

注意,这个答案涵盖了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})
  })
})
Run Code Online (Sandbox Code Playgroud)

routes模块出口路由的列表:

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

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

每次向服务器发出请求时,都会创建一个使用Router实例配置的一次性实例作为其静态位置,该实例将根据路由树进行解析,以设置相应的匹配路由,并使用顶层回调要呈现的路由处理程序以及在每个级别匹配的子路由的记录.当您使用<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)
})
Run Code Online (Sandbox Code Playgroud)

当您调用时Router.run(),它会在幕后为您创建一个路由器实例,每次您在应用程序中导航时都会重复使用该实例,因为URL可以在客户端上是动态的,而不是在单个请求具有的请求的服务器上固定的URL.

在这种情况下,我们使用HistoryLocation,它使用HistoryAPI,以确保当你点击后退/前进按钮正确的事情发生.还有一个HashLocation更改URL hash以创建历史记录条目并侦听window.onhashchange事件以触发导航.

当你使用react-router的<Link>组件时,你给它一个toprop,它是路由的名称,加上路由所需的任何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);
    }
  },
Run Code Online (Sandbox Code Playgroud)

对于常规链接,这最终会调用location.push()您正在使用的任何位置类型,它处理设置历史记录的详细信息,因此使用后退和前进按钮进行导航将起作用,然后回调router.handleLocationChange()以让路由器知道它可以继续转换到新的URL路径.

然后,路由器router.dispatch()使用新URL 调用自己的方法,该URL处理确定哪些配置的路由与URL匹配的详细信息,然后调用匹配路由的任何转换挂钩.您可以在任何路由处理程序上实现这些转换挂钩,以便在路由即将导航或导航到路径时执行某些操作,并且能够在不符合您喜欢的情况下中止转换.

如果转换未中止,则最后一步是Router.run()使用顶级处理程序组件调用您给出的回调,并使用URL和匹配路由的所有详细信息调用状态对象.顶级处理程序组件实际上是Router实例本身,它处理呈现匹配的最顶层路由处理程序.

每次导航到客户端上的新URL时,都会重新运行上述过程.

示例项目

  • 路由模块中的内容`var routes = require('./ routes')`它是一个路由列表吗?我使用过Express路由器,但这个例子在SO上似乎是用React Router设置服务器端渲染的唯一例子,所以如果它是一个完整的代码示例会很好 (9认同)
  • 所以我可以说客户端路由是由javascript(反应路由器代码)处理的.每当我按下浏览器地址栏上的Enter键或刷新页面或禁用JS时,服务器端都会处理路由.另一方面,当javascript在当前页面上就绪时,路由将由客户端处理.我理解正确吗? (3认同)
  • 很棒的回答,非常感谢! (2认同)
  • 它应该是一个路线列表.我将添加一个关于它的说明和一些示例项目的链接. (2认同)
  • 因此,如果react-router负责服务器端路由,那么谁与数据库进行通信?服务器端路由会发生什么?想象一下,我们想为本机移动应用程序提供REST API.谁负责这件事? (2认同)

tom*_*tom 26

使用1.0,React-Router依赖于历史模块作为peerDependency.该模块处理浏览器中的路由.默认情况下,React-Router使用HTML5 History API(pushState,replaceState),但您可以将其配置为使用基于散列的路由(请参阅下文)

路由处理现在在幕后完成,当路由改变时,ReactRouter将新的支持向下发送给Route处理程序.例如,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)
Run Code Online (Sandbox Code Playgroud)

客户端(基于散列的路由)

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)
Run Code Online (Sandbox Code Playgroud)

服务器

在服务器上,我们可以使用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')
    }
  })
})
Run Code Online (Sandbox Code Playgroud)