Javascript 客户端路由(使用 react-router)和服务器端路由
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28553904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Client Routing (using react-router) and Server-Side Routing
提问by heartmon
I have been thinking and I am confused with the routing between Client and Server. Suppose I use ReactJS for server-side rendering before sending the request back to web browser, and use react-router as a client-side routing to switch between pages without refreshing as SPA.
我一直在思考,我对Client和Server之间的路由感到困惑。假设我在将请求发送回 Web 浏览器之前使用 ReactJS 进行服务器端渲染,并使用 react-router 作为客户端路由在页面之间切换而不像 SPA 那样刷新。
What comes to mind is:
想到的是:
- How are the routes interpreted? For example, a request from Home page (
/home) to Posts page (/posts) - Where does the routing go, on server-side or client?
- How does it know how it is processed?
- 路线如何解释?例如,从主页 (
/home) 到帖子页面 (/posts)的请求 - 路由去哪里,在服务器端还是客户端?
- 它如何知道它是如何处理的?
回答by Jonny Buchanan
Note, this answer covers React Router version 0.13.x - the upcoming version 1.0looks like it will have significantly different implementation details
请注意,此答案涵盖 React Router 0.13.x 版 -即将推出的 1.0 版看起来将具有显着不同的实现细节
Server
服务器
This is a minimal server.jswith 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})
})
})
Where the routesmodule exports a list of Routes:
凡routes模块出口路由的列表:
var React = require('react')
var {DefaultRoute, NotFoundRoute, Route} = require('react-router')
module.exports = [
<Route path="/" handler={require('./components/App')}>
{/* ... */}
</Route>
]
Every time a request is made to the server, you create a single-use Routerinstance configured with the incoming URL as its static location, which is resolved against the tree of routes to set up the appropriate matched routes, calling back with the top-level route handler to be rendered and a record of which child routes matched at each level. This is what's consulted when you use the <RouteHandler>component within a route handling component to render a child route which was matched.
每次向服务器发出请求时,您都会创建一个使用Router传入 URL 作为其静态位置配置的一次性实例,该实例根据路由树进行解析以设置适当的匹配路由,并使用顶层回调要呈现的路由处理程序以及在每个级别匹配哪些子路由的记录。这是当您<RouteHandler>在路由处理组件中使用组件来呈现匹配的子路由时所参考的内容。
If the user has JavaScript turned off, or it's being slow to load, any links they click on will hit the server again, which is resolved again as above.
如果用户关闭了 JavaScript,或者加载速度很慢,他们点击的任何链接都会再次访问服务器,如上所示再次解决了这个问题。
Client
客户
This is a minimal client.jswith react-router (re-using the same routes module):
这是client.jsreact-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)
})
When you call Router.run(), it creates a Router instance for you behind the scenes, which is re-used every time you navigate around the app, as the URL can be dynamic on the client, as opposed to on the server where a single request has a fixed URL.
当您调用 时Router.run(),它会在幕后为您创建一个 Router 实例,每次您浏览应用程序时都会重复使用该实例,因为 URL 在客户端上可以是动态的,而不是在服务器上,单个请求具有固定网址。
In this case, we're using the HistoryLocation, which uses the HistoryAPIto make sure the right thing happens when you hit the back/forward button. There's also a HashLocationwhich changes the URL hashto make history entries and listens to the window.onhashchangeevent to trigger navigation.
在这种情况下,我们使用HistoryLocation,它使用HistoryAPI来确保在您点击后退/前进按钮时发生正确的事情。还有一个HashLocation可以更改 URLhash以创建历史条目并侦听window.onhashchange事件以触发导航。
When you use react-router's <Link>component, you give it a toprop which is the name of a route, plus any paramsand querydata the route needs. The <a>rendered by this component has an onClickhandler which ultimately calls router.transitionTo()on the router instance with the props you gave the link, which looks like this:
当你使用 react-router 的<Link>组件时,你给它一个toprop,它是路由的名称,加上路由需要的任何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);
}
},
For a regular link this ultimately calls location.push()on whichever Location type you're using, which handles the details of setting up history so navigating with the back and forward buttons will work, then calls back to router.handleLocationChange()to let the router know it can proceed with transitioning to the new URL path.
对于常规链接,这最终会调用location.push()您使用的任何位置类型,它处理设置历史记录的详细信息,因此使用后退和前进按钮导航将起作用,然后回调router.handleLocationChange()以让路由器知道它可以继续过渡到新的 URL 路径。
The router then calls its own router.dispatch()method with the new URL, which handles the details of determining which of the configured routes match the URL, then calls any transition hookspresent for the matched routes. You can implement these transition hooks on any of your route handlers to take some action when a route is about to be navigated away from or navigated to, with the ability to abort the transition if things aren't to your liking.
然后路由器router.dispatch()用新的 URL调用它自己的方法,该方法处理确定哪些配置的路由与 URL 匹配的细节,然后调用匹配路由存在的任何转换钩子。您可以在任何路由处理程序上实现这些转换钩子,以便在路由即将离开或导航到时采取一些操作,如果事情不符合您的喜好,则可以中止转换。
If the transition wasn't aborted, the final step is to call the callback you gave to Router.run()with the top-level handler component and a state object with all the details of the URL and the matched routes. The top-level handler component is actually the Routerinstance itself, which handles rendering the top-most route handler which was matched.
如果转换没有中止,最后一步是Router.run()使用顶级处理程序组件和包含 URL 和匹配路由的所有详细信息的状态对象调用您提供的回调。顶级处理程序组件实际上是Router实例本身,它处理渲染匹配的最顶级路由处理程序。
The above process is re-run every time you navigate to a new URL on the client.
每次导航到客户端上的新 URL 时,都会重新运行上述过程。
Example projects
示例项目
回答by tom
With 1.0, React-Router depends on the historymodule as a peerDependency. This module deals with routing in the browser. By default React-Router uses the HTML5 History API (pushState, replaceState), but you can configure it to use hash-based routing (see below)
在 1.0 中,React-Router 依赖于历史模块作为 peerDependency。该模块处理浏览器中的路由。默认情况下,React-Router 使用 HTML5 History API ( pushState, replaceState),但您可以将其配置为使用基于哈希的路由(见下文)
The route handling is now done behind the scenes, and ReactRouter sends new props down to the Route handlers when the route changes. The Router has a new onUpdateprop callback whenever a route changes, useful for pageview tracking, or updating the <title>, for example.
路由处理现在在幕后完成,当路由改变时,ReactRouter 将新的 props 发送到 Route 处理程序。onUpdate每当路由发生变化时,路由器都会有一个新的道具回调,这对于网页浏览跟踪或更新<title>.
Client (HTML5 routing)
客户端(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)
Client (hash-based routing)
客户端(基于哈希的路由)
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)
Server
服务器
On the server, we can use ReactRouter.match, this is taken from the server rendering guide
在服务器上,我们可以使用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')
}
})
})

