本文翻译自: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
复制代码
接下来将使用 Route
。Route
是 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 在一个组件内部渲染 navbar
和 Route
你可能会注意到咱们都是在硬编码 URL,而不是经过当前嵌套的位置来动态建立。React Router 在渲染一个组件的时候,它会传递三个东西:match
、location
和 history
。在这个例子中,咱们想要的是 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。咱们能够建立一个渲染文本的组件或者使用 Route
的 render
属性:
<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 开发者。
想要与做者交流?点击 这里查看原文 了解更多