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 開發者。

想要與做者交流?點擊 這裏查看原文 瞭解更多

相關文章
相關標籤/搜索