客户端路由(使用反应路由器)和服务器端路由客户端(HTML5 路由)客户端(基于哈希的路由)服务器
我一直在思考,我对客户端和服务器之间的路由感到困惑。假设我在将请求发送回Web浏览器之前使用ReactJS进行服务器端渲染,并使用react-router作为客户端路由在页面之间切换,而无需刷新为SPA。
我想到的是:
- 如何解释路线?例如,从主页 () 到“帖子”页面 (
/home
/posts
) - 路由在服务器端还是客户端的位置?
- 它如何知道它是如何处理的?
我一直在思考,我对客户端和服务器之间的路由感到困惑。假设我在将请求发送回Web浏览器之前使用ReactJS进行服务器端渲染,并使用react-router作为客户端路由在页面之间切换,而无需刷新为SPA。
我想到的是:
/home
/posts
)请注意,这个答案涵盖了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})
})
})
模块导出路由列表的位置:routes
var React = require('react')
var {DefaultRoute, NotFoundRoute, Route} = require('react-router')
module.exports = [
<Route path="/" handler={require('./components/App')}>
{/* ... */}
</Route>
]
每次向服务器发出请求时,您都会创建一个配置了一次性实例,该实例使用传入 URL 作为其静态位置,该实例根据路由树进行解析以设置适当的匹配路由,并使用要呈现的顶级路由处理程序回调该实例,并记录每个级别匹配的子路由。当您使用路由处理组件中的组件来呈现匹配的子路由时,会参考此信息。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)
})
当您调用 时,它会在后台为您创建一个 Router 实例,每次您在应用程序中导航时都会重复使用该实例,因为 URL 在客户端上可以是动态的,而不是在单个请求具有固定 URL 的服务器上。Router.run()
在本例中,我们使用 ,它使用历史记录
API 来确保在点击后退/前进按钮时发生正确的事情。还有一个更改URL以创建历史记录条目并侦听window.onhashchange
事件以触发导航。HistoryLocation
HashLocation
hash
当你使用react-router的组件时,你给它一个道具,它是路由的名称,加上路由所需的任何数据。此组件呈现的具有一个处理程序,该处理程序最终使用您为链接提供的 props 调用路由器实例,如下所示:<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);
}
},
对于常规链接,这最终会调用您正在使用的任何位置类型,该类型处理设置历史记录的详细信息,因此使用后退和前进按钮进行导航将起作用,然后回调以让路由器知道它可以继续转换到新的URL路径。location.push()
router.handleLocationChange()
然后,路由器使用新 URL 调用自己的方法,该方法处理确定哪些配置的路由与 URL 匹配的详细信息,然后调用匹配路由存在的任何转换挂钩。您可以在任何路由处理程序上实现这些转换挂钩,以便在路由即将被导航离开或导航到时采取一些操作,如果事情不符合您的喜好,则可以中止转换。router.dispatch()
如果转换未中止,则最后一步是调用您使用的顶级处理程序组件和状态对象(其中包含 URL 和匹配路由的所有详细信息)提供给的回调。顶级处理程序组件实际上是实例本身,它处理已匹配的最顶层路由处理程序的呈现。Router.run()
Router
每次导航到客户端上的新 URL 时,都会重新运行上述过程。
在 1.0 中,React-Router 依赖于历史模块作为对等独立性。此模块处理浏览器中的路由。默认情况下,React-Router使用HTML5 History API(,),但您可以将其配置为使用基于哈希的路由(见下文)pushState
replaceState
路由处理现在在幕后完成,ReactRouter 会在路由更改时将新道具向下发送到路由处理程序。每当路由更改时,路由器都会有一个新的 prop 回调,这对于网页浏览量跟踪或更新等非常有用。onUpdate
<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')
}
})
})