我一直在思考并且对客户端和服务器之间的路由感到困惑。假设我使用ReactJS进行服务器端渲染,然后将请求发送回Web浏览器,并使用React-Router作为客户端路由,在SPA中实现页面切换而无需刷新。
我想到的问题是:
- 路由是如何被解释的?例如,从主页 (
/home
) 请求到帖子页 (/posts
) - 路由在服务器端还是客户端上?
- 它如何知道如何处理?
我一直在思考并且对客户端和服务器之间的路由感到困惑。假设我使用ReactJS进行服务器端渲染,然后将请求发送回Web浏览器,并使用React-Router作为客户端路由,在SPA中实现页面切换而无需刷新。
我想到的问题是:
/home
) 请求到帖子页 (/posts
)这是一个带有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>
组件呈现匹配的子路由时,这是要查阅的内容。client.js
,带有react-router(重用相同的路由模块):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的哈希
,以使历史记录条目,并侦听window.onhashchange
事件来触发导航。<Link>
组件时,你需要给它一个to
属性,这个属性是路由的名称,加上路由所需的任何params
和query
数据。这个组件渲染出来的<a>
有一个onClick
处理程序,最终会在路由实例上调用router.transitionTo()
方法,并将你给链接的props传递进去,看起来像这样: /**
* 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匹配的详细信息,然后调用匹配路由中存在的任何transition hooks。您可以在任何路由处理程序上实现这些过渡钩子,以在即将从路由导航或导航到路由时执行某些操作,并能够根据需要中止过渡。Router.run()
的回调函数。顶级处理程序组件实际上是Router
实例本身,它处理呈现与匹配的顶级路由处理程序。var routes = require('./routes')
模块中包含了什么?它是一个路由列表吗?我已经使用过 Express 路由器,但在 SO 上的这个例子似乎是使用 React Router 设置服务器端渲染的唯一例子,所以如果有完整的代码示例就更好了。 - svnmreact-router
版本。请进行更新。 - oleh.meleshkoReact-Router 1.0现在作为对等的依赖项使用history模块来处理浏览器路由。默认情况下,React-Router使用HTML5 History API (pushState
, replaceState
)进行路由处理,但你可以配置它使用基于哈希值的路由(见下文)。
现在路由处理在幕后完成,当路由变化时,ReactRouter会向路由处理程序发送新的props。路由器有一个新的onUpdate
prop回调,每当路由改变时都会调用,非常适用于页面查看跟踪或更新<title>
等操作。
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')
}
})
})