客户端路由(使用反应
我一直在想,我对客户端和服务器之间的路由感到困惑。 假设我在将请求发送回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
模块在哪里输出routes
列表:
var React = require('react')
var {DefaultRoute, NotFoundRoute, Route} = require('react-router')
module.exports = [
<Route path="/" handler={require('./components/App')}>
{/* ... */}
</Route>
]
每次向服务器发出请求时,都会创建一个配置为传入URL的单次使用的Router
实例作为其静态位置,该Router
实例将根据路由树进行解析以设置适当的匹配路由,级别路由处理程序以及每个级别匹配哪些子路由的记录。 这是您在路由处理组件中使用<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
支撑这是一个路由的名称,加上任何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处理确定哪些配置的路由与URL匹配的详细信息,然后调用匹配路由的所有过渡挂钩。 您可以在任何路由处理程序上实现这些转换挂钩,以便在路线即将导航离开或导航到时执行一些操作,并且如果事情不符合您的需要,可以中止转换。
如果转换没有中止,最后一步是使用顶级处理程序组件和一个包含URL和匹配路由所有细节的状态对象来调用您给Router.run()
的回调。 顶级处理程序组件实际上是Router
实例本身,它处理渲染匹配的最顶级路由处理程序。
每当您导航到客户端上的新URL时,上述过程都会重新运行。
示例项目
在1.0中,React-Router依赖历史模块作为peerDependency。 该模块处理浏览器中的路由。 默认情况下做出反应,路由器使用HTML5历史API( pushState
, replaceState
),但你可以将其配置为使用基于散列的路由(见下文)
路由处理现在做幕后,和ReactRouter发送新道具到路由处理程序时,路由发生变化。 路由器有一个新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)
客户端(基于散列的路由)
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')
}
})
})
链接地址: http://www.djcxy.com/p/52121.html