React Router 4 简介及其背后的路由哲学

本文翻译自:An Introduction to React Router v4 and its Philosophy Toward Routingjavascript

译文地址:github.com/rccoder/blo…java

本文翻译自:An Introduction to React Router v4 and its Philosophy Toward Routingreact

React Router 4 引入了一种基于 component 的动态路由。git

这篇文章中会讨论思考 React Router 背后的哲学,同时也会经过分析 React Router 文档中的示例代码来介绍一下它的语法。github

更多 React Router 的介绍请戳 这里bash

不想看文字版本还能够点击 这里 看视频。react-router

若是你这几年一直在密切关注着 React,你应该会注意到 React Router 已经经历了多个版本的迭代。今天的 v4 版本更是一个巨大的改变。app

产生这些变化的缘由都是很是天然的 —— 今天 React 的开发者相比于 React Router 刚产生的时候更有经验。在 2014 年的时候,人人都是新手。没有人会想到 component 的概念在不足一年的 React 中会有这么重要。dom

由于上面的缘由,React Router 的第一个 commit 是这样的:ui

当时,React Router 的做者 Michael 和 Ryan 都有着 Ember 的开发经验。天然着,React Router 的第一个版本和 Ember 的路由有点类似 —— 都是静态着去创建路由,做为应用初始化的一部分。

这种路由的概念就和熟悉的 Express、Angular、Ember 的概念一致。甚至在 React Router 4 release 以前,使用的也是静态路由。

使用静态路由的时候每每会有这样的一段代码写在 routes.js 里面:

const routes = (
  <Router>
    <Route path='/' component={Main}>
      <IndexRoute component={Home} />
      <Route path='playerOne' component={Prompt} />
      <Route path='playerTwo/:playerOne' component={Prompt} />
      <Route path='battle' component={ConfirmBattle} />
      <Route path='results' component={Results} />
      <Route onEnter={checkAuth} path='dashboard' component={Dashboard} />
    </Route>
  </Router>
)
export default routes
复制代码

而后在初始化应用的时候,会把路由导入,而后渲染:

// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import routes from './config/routes'
ReactDOM.render(routes, document.getElementById('app'))
复制代码

这就产生了一个问题:静态路由很差用吗?

答案显示是 “不,好用”。但你们仍然会以为静态路由模式不是 React 的风格。

在 React Router 诞生以来,其做者不只对构建复杂应用的路由有了更多的经验,同时对 React 自己也有了更多的理解。后面在工做与讨论中发现,React Router 的 API 和 React 背后遵循的一些原则有点背道相驰。再回过头来看下前面的代码,咱们将一个 onEnter 的 prop 传给 Route 组件。

<Route onEnter={checkAuth} path='dashboard' component={Dashboard} />
复制代码

这段代码是指在渲染 dashboard 组件的时候须要通过一层鉴权。这听起来像是在 Dashboard 的 componentDidMount 生命周期里去作一些事情?的确就是这样。

在 React Router 4 以前,它所作的事情有点超出路由这个层面。React Router 4 针对这些问题做出了修正,使得他和 React 相处的更好。若是你了解 React 以及它使用 component 的优点,React Router 4 会让你感受更加亲切 —— 你须要先忘记你对传统静态路由的一些了解。

如今还有一个问题是:React Router 4 到底作了什么再也不让他和 React 有冲突呢?答案是他抛弃了静态路由而开始偏向于使用动态路由,全部的 API 都是基于 component。这也就意味着声明路由的时候就像普通组件如出一辙。

简单看下下面的例子:

先从最基本的代码开始,而后为其添加路由功能。

import React, { Component } from 'react'
class App extends Component {
  render() {
    return (
      <div> React Rotuer Course </div>
    )
  }
}
export default App
复制代码

和我前面所说的同样,React Router 4 就是一个普通的 component。所以第一件事是 import 咱们所须要的东西。

import {
  BrowserRouter as Router,
  Route,
  Link,
} from 'react-router-dom'
复制代码

这里须要注意一些事情。首先,咱们 import 了 BrowserRouter 并将之从新命名为 Router。这虽然不是必须的,但在代码中仍是很常见的。BrowerRouter 所作的事情就是容许 React Router 将应用的路由信息传给任何他须要的组件(经过 context)。所以,要让 React Router 正常工做,须要在应用程序的根节点中渲染 BrowerRouter

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link,
} from 'react-router-dom'
class App extends Component {
  render() {
    return (
      <Router> <div> React Rotuer Course </div> </Router>
    )
  }
}
export default App
复制代码

接下来将使用 RouteRoute 是 React Router 4 背后的支撑。当应用程序的 location 匹配到某个路由的时候,Route 将渲染指定的 component,不然渲染 null。举个例子,当咱们应用的路由是 / 的时候要渲染一个 Home 组件,代码会长的像下面这样:

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link,
} from 'react-router-dom'
const Home = () => (
  <h2>Home</h2>
)
class App extends Component {
  render() {
    return (
      <Router> <div> <Route path='/' component={Home} /> </div> </Router> ) } } export default App 复制代码

上面的代码中,若是咱们的路径是 / 的时候,将看见 Home 组件。若是不是的话,什么都看不到(Route 会渲染 null)。

下面将加入更多的路由:

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link,
} from 'react-router-dom'
const Home = () => (
  <div> <h2>Home</h2> </div>
)
const About = () => (
  <div> <h2>About</h2> </div>
)
const Topics = () => (
  <div> <h2>Topics</h2> </div>
)
class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <Route path='/' component={Home} />
          <Route path='/about' component={About} />
          <Route path='/topics' component={Topics} />
        </div>
      </Router>
    )
  }
}
export default App
复制代码

如上,若是想在应用中加入更多的路由,只须要渲染更多的 Route 组件。若是你尚未忘记静态路由,可能会对渲染路由这种事情感到奇怪。

你只须要记住 Route 只是一个具备渲染方法的普通 React 组件,该渲染方法渲染组件仍是 null 取决于 path 是否匹配。由于在上面的例子中,要么渲染组件,要么渲染 null

在上面代码中有一个点须要注意:运行这个程序的时候,前往 about 路径,About 组件和 Home 组件都会被渲染。这是由于即便 / 不是彻底匹配的,但仍旧会认为是部分匹配,因此 Home 组件也会被渲染。为了解决这种问题,须要在 /Route 中添加一个 exact 的 prop,来确保只有彻底匹配的时候才会渲染。

<Route exact path='/' component={Home} />
复制代码

如今咱们经过应用的 location 在动态的渲染 UI,下一件事情就是如何去改变应用的 location。这正是 Link 组件所要作的事情,它是一个容许用户声明性的浏览应用的组件。如今,使用 Link 添加一个简单的导航吧。

render() {
  return (
    <Router>
      <div>
        <ul>
          <li><Link to='/'>Home</Link></li>
          <li><Link to='/about'>About</Link></li>
          <li><Link to='/topics'>Topics</Link></li>
        </ul>
        <Route path='/' component={Home} />
        <Route path='/about' component={About} />
        <Route path='/topics' component={Topics} />
      </div>
    </Router>
  )
}
复制代码

事实上如今已经介绍完了 React Router 4 的基本操做。咱们基于应用的 location 使用 Route 去渲染不一样的组件,而且经过 Link 组件来更改应用的 location。

更深一点,一块儿看看嵌套的路由。嵌套路由是 React Router 以前版本的一个基础功能,今天它仍然也是。与以前版本相比,最大的区别就是如今建立嵌套路由的方式。在以前的版本中,只须要在路由配置中嵌套的使用路由,但今天有雨 React Router 4 是动态路由,因此这样作是行不通的。可是就我而言,以为 React Router 4 的嵌套路由比以前版本的更加直接。再次强调一下:忘记以前你对静态路由了解的一切。

再看一下咱们的例子,若是咱们想要 Topics 组件渲染一个嵌套的 导航和其余的一些路由该怎么作呢?不须要很复杂,就像嵌套一个 div 同样,你只须要嵌套使用 Route

const Topic = () => {
  <div>
    <h3>TOPIC</h3>
  </div>
}
const Topics = () => (
  <div>
    <h2>Topics</h2>
    <ul>
      <li>
        <Link to={`/topics/rendering`}>
          Rendering with React
        </Link>
      </li>
      <li>
        <Link to={`/topics/components`}>
          Components
        </Link>
      </li>
      <li>
        <Link to={`/topics/props-v-state`}>
          Props v. State
        </Link>
      </li>
    </ul>
    <Route path={`/topics/rendering`} component={Topic} />
    <Route path={`/topics/components`} component={Topic} />
    <Route path={`/topics/props-v-state`} component={Topic} />
  </div>
)
复制代码

如今当用户导航到 /topics 时,将看到一个嵌套的导航栏,UI 也会随着 location 的变化而自动改变。惟一的区别是咱们如今正在经过 React Router 在一个组件内部渲染 navbarRoute

你可能会注意到咱们都是在硬编码 URL,而不是经过当前嵌套的位置来动态建立。React Router 在渲染一个组件的时候,它会传递三个东西:matchlocationhistory。在这个例子中,咱们想要的是 match.url,它会给咱们当前 URL 中匹配的部分(在咱们的例子中,//topics)。因此在任何咱们很差硬编码 /topic 的地方,均可以使用 match.url 替换。

const Topic = () => {
  <div>
    <h3>TOPIC</h3>
  </div>
}
const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <ul>
      <li>
        <Link to={`${match.url}/rendering`}>
          Rendering with React
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/components`}>
          Components
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/props-v-state`}>
          Props v. State
        </Link>
      </li>
    </ul>
    <Route path={`${match.url}/rendering`} component={Topic} />
    <Route path={`${match.url}/components`} component={Topic} />
    <Route path={`${match.url}/props-v-state`} component={Topic} />
  </div>
)
复制代码

还有另一件事情你可能会注意到:即便渲染相同的组件,咱们也在渲染三个不一样的 Route,他们以前惟一的区别是嵌套的 URL。下面是使用 url 参数的经典例子。

const Topics = ({ match }) => (
  <div> ... <Route path={`${match.url}/:topicId`} component={Topic} /> </div> ) 复制代码

React Router 渲染 Topic 组件时,使用了以前介绍过的 match 属性,与此相似,还可使用 match.params 下的 topicId

const Topic = ({ match }) => (
  <div> <h3>{match.params.topicId}</h3> </div>
)
复制代码

最后,当咱们处于 /topics 路由时,若是某个主题还未被选中,咱们想渲染一个文字,好比:Please select a topic。咱们能够建立一个渲染文本的组件或者使用 Routerender 属性:

<Route 
  exact 
  path={match.url} 
  render={() => ( <h3>Please select a topic.</h3> )}
/>
复制代码

就这样,咱们酷酷的代码会长得像这样:

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'
const Home = () => (
  <div> <h2>Home</h2> </div>
)
const About = () => (
  <div> <h2>About</h2> </div>
)
const Topic = ({ match }) => (
  <div> <h3>{match.params.topicId}</h3> </div>
)
const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <ul>
      <li>
        <Link to={`${match.url}/rendering`}>
          Rendering with React
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/components`}>
          Components
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/props-v-state`}>
          Props v. State
        </Link>
      </li>
    </ul>
    <Route path={`${match.url}/:topicId`} component={Topic}/>
    <Route exact path={match.url} render={() => (
      <h3>Please select a topic.</h3>
    )}/>
  </div>
)
class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/topics">Topics</Link></li>
          </ul>
          <hr/>
          <Route exact path="/" component={Home}/>
          <Route path="/about" component={About}/>
          <Route path="/topics" component={Topics}/>
        </div>
      </Router>
    )
  }
}
export default App
复制代码

React Router 是一个以 component 为 API 的 Router,是一个真正意义上的 React Router。我相信 React 会让你成为更好的 JavaScript 开发者,而 React Router 4 会让你成为更好的 React 开发者。

想要与做者交流?点击 这里查看原文 了解更多

相关文章
相关标签/搜索