本文翻譯自: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 開發者。
想要與做者交流?點擊 這裏查看原文 瞭解更多