• 简介
    • 不使用 React Router
    • 使用 React Router 后
  • 添加更多的 UI
    • 获取 URL 参数

    简介

    React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。

    为了向你说明 React Router 解决的问题,让我们先来创建一个不使用它的应用。所有文档中的示例代码都会使用 ES6/ES2015 语法和语言特性。

    不使用 React Router

    1. import React from 'react'
    2. import { render } from 'react-dom'
    3. const About = React.createClass({/*...*/})
    4. const Inbox = React.createClass({/*...*/})
    5. const Home = React.createClass({/*...*/})
    6. const App = React.createClass({
    7. getInitialState() {
    8. return {
    9. route: window.location.hash.substr(1)
    10. }
    11. },
    12. componentDidMount() {
    13. window.addEventListener('hashchange', () => {
    14. this.setState({
    15. route: window.location.hash.substr(1)
    16. })
    17. })
    18. },
    19. render() {
    20. let Child
    21. switch (this.state.route) {
    22. case '/about': Child = About; break;
    23. case '/inbox': Child = Inbox; break;
    24. default: Child = Home;
    25. }
    26. return (
    27. <div>
    28. <h1>App</h1>
    29. <ul>
    30. <li><a href="#/about">About</a></li>
    31. <li><a href="#/inbox">Inbox</a></li>
    32. </ul>
    33. <Child/>
    34. </div>
    35. )
    36. }
    37. })
    38. React.render(<App />, document.body)

    当 URL 的 hash 部分(指的是 # 后的部分)变化后,<App> 会根据 this.state.route 来渲染不同的 <Child>。看起来很直接,但它很快就会变得复杂起来。

    现在设想一下 Inbox 下面嵌套一些分别对应于不同 URL 的 UI 组件,就像下面这样的列表-详情视图:

    1. path: /inbox/messages/1234
    2. +---------+------------+------------------------+
    3. | About | Inbox | |
    4. +---------+ +------------------------+
    5. | Compose Reply Reply All Archive |
    6. +-----------------------------------------------+
    7. |Movie tomorrow| |
    8. +--------------+ Subject: TPS Report |
    9. |TPS Report From: boss@big.co |
    10. +--------------+ |
    11. |New Pull Reque| So ... |
    12. +--------------+ |
    13. |... | |
    14. +--------------+--------------------------------+

    还可能有一个状态页,用于在没有选择 message 时展示:

    1. path: /inbox
    2. +---------+------------+------------------------+
    3. | About | Inbox | |
    4. +---------+ +------------------------+
    5. | Compose Reply Reply All Archive |
    6. +-----------------------------------------------+
    7. |Movie tomorrow| |
    8. +--------------+ 10 Unread Messages |
    9. |TPS Report | 22 drafts |
    10. +--------------+ |
    11. |New Pull Reque| |
    12. +--------------+ |
    13. |... | |
    14. +--------------+--------------------------------+

    为了让我们的 URL 解析变得更智能,我们需要编写很多代码来实现指定 URL 应该渲染哪一个嵌套的 UI 组件分支:App -> About, App -> Inbox -> Messages -> Message, App -> Inbox -> Messages -> Stats,等等。

    使用 React Router 后

    让我们用 React Router 重构这个应用。

    1. import React from 'react'
    2. import { render } from 'react-dom'
    3. // 首先我们需要导入一些组件...
    4. import { Router, Route, Link } from 'react-router'
    5. // 然后我们从应用中删除一堆代码和
    6. // 增加一些 <Link> 元素...
    7. const App = React.createClass({
    8. render() {
    9. return (
    10. <div>
    11. <h1>App</h1>
    12. {/* 把 <a> 变成 <Link> */}
    13. <ul>
    14. <li><Link to="/about">About</Link></li>
    15. <li><Link to="/inbox">Inbox</Link></li>
    16. </ul>
    17. {/*
    18. 接着用 `this.props.children` 替换 `<Child>`
    19. router 会帮我们找到这个 children
    20. */}
    21. {this.props.children}
    22. </div>
    23. )
    24. }
    25. })
    26. // 最后,我们用一些 <Route> 来渲染 <Router>。
    27. // 这些就是路由提供的我们想要的东西。
    28. React.render((
    29. <Router>
    30. <Route path="/" component={App}>
    31. <Route path="about" component={About} />
    32. <Route path="inbox" component={Inbox} />
    33. </Route>
    34. </Router>
    35. ), document.body)

    React Router 知道如何为我们搭建嵌套的 UI,因此我们不用手动找出需要渲染哪些 <Child> 组件。举个例子,对于一个完整的 /about 路径,React Router 会搭建出 <App><About /></App>

    在内部,router 会将你树级嵌套格式的 <Route> 转变成路由配置。但如果你不熟悉 JSX,你也可以用普通对象来替代:

    1. const routes = {
    2. path: '/',
    3. component: App,
    4. childRoutes: [
    5. { path: 'about', component: About },
    6. { path: 'inbox', component: Inbox },
    7. ]
    8. }
    9. React.render(<Router routes={routes} />, document.body)

    添加更多的 UI

    好了,现在我们准备在 inbox UI 内嵌套 inbox messages。

    1. // 新建一个组件让其在 Inbox 内部渲染
    2. const Message = React.createClass({
    3. render() {
    4. return <h3>Message</h3>
    5. }
    6. })
    7. const Inbox = React.createClass({
    8. render() {
    9. return (
    10. <div>
    11. <h2>Inbox</h2>
    12. {/* 渲染这个 child 路由组件 */}
    13. {this.props.children || "Welcome to your Inbox"}
    14. </div>
    15. )
    16. }
    17. })
    18. React.render((
    19. <Router>
    20. <Route path="/" component={App}>
    21. <Route path="about" component={About} />
    22. <Route path="inbox" component={Inbox}>
    23. {/* 添加一个路由,嵌套进我们想要嵌套的 UI 里 */}
    24. <Route path="messages/:id" component={Message} />
    25. </Route>
    26. </Route>
    27. </Router>
    28. ), document.body)

    现在访问 URL inbox/messages/Jkei3c32 将会匹配到一个新的路由,并且它成功指向了 App -> Inbox -> Message 这个 UI 的分支。

    1. <App>
    2. <Inbox>
    3. <Message params={ {id: 'Jkei3c32'} } />
    4. </Inbox>
    5. </App>

    获取 URL 参数

    为了从服务器获取 message 数据,我们首先需要知道它的信息。当渲染组件时,React Router 会自动向 Route 组件中注入一些有用的信息,尤其是路径中动态部分的参数。我们的例子中,它指的是 :id

    1. const Message = React.createClass({
    2. componentDidMount() {
    3. // 来自于路径 `/inbox/messages/:id`
    4. const id = this.props.params.id
    5. fetchMessage(id, function (err, message) {
    6. this.setState({ message: message })
    7. })
    8. },
    9. // ...
    10. })

    你也可以通过 query 字符串来访问参数。比如你访问 /foo?bar=baz,你可以通过访问 this.props.location.query.bar 从 Route 组件中获得 "baz" 的值。

    这就是 React Router 的奥秘。应用的 UI 以盒子中嵌套盒子的方式来表现;然后你可以让这些盒子与 URL 始终保持同步,而且很容易地把它们链接起来。

    这个关于 路由配置 的文档深入地描述了 router 的功能。