请注意,此答案涵盖 React Router 版本 0.13.x - upcoming version 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() 时,它会在后台为您创建一个路由器实例,每次您在应用程序中导航时都会重复使用该实例,因为 URL 在客户端上可以是动态的,而不是在服务器上单个请求具有固定 URL。
在这种情况下,我们使用HistoryLocation,它使用History API 来确保在您点击后退/前进按钮时发生正确的事情。还有一个 HashLocation 更改 URL hash 以创建历史条目并侦听 window.onhashchange 事件以触发导航。
当你使用 react-router 的 <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 路径。
路由器然后使用新 URL 调用它自己的 router.dispatch() 方法,该方法处理确定哪些配置的路由与 URL 匹配的详细信息,然后调用匹配路由的任何 transition hooks。您可以在任何路由处理程序上实现这些转换钩子,以便在路由即将被导航离开或导航到时采取一些行动,如果事情不符合您的喜好,则可以中止转换。
如果转换没有中止,最后一步是使用顶级处理程序组件和包含 URL 和匹配路由的所有详细信息的状态对象调用您提供给 Router.run() 的回调。顶级处理程序组件实际上是Router 实例本身,它处理渲染匹配的最顶级路由处理程序。
每次您导航到客户端上的新 URL 时,都会重新运行上述过程。
示例项目