React Router 4 簡易入門

原文react

React Router4是一個流行的純React重寫的包。如今的版本中已不須要路由配置,如今一切皆組件。git

本文涵蓋了開始使用React Router構建網站所須要的一切知識。咱們將會爲本地運動隊製做一個網站。github

代碼

想看網站最終效果,查看demoweb

安裝

React Router被拆分紅三個包:react-router,react-router-domreact-router-nativereact-router提供核心的路由組件與函數。其他兩個則提供運行環境(即瀏覽器與react-native)所需的特定組件。正則表達式

進行網站(將會運行在瀏覽器環境中)構建,咱們應當安裝react-router-domreact-router-dom暴露出react-router中暴露的對象與方法,所以你只須要安裝並引用react-router-dom便可。npm

npm install --save react-router-dom

路由器(Router)

在你開始項目前,你須要決定你使用的路由器的類型。對於網頁項目,存在<BrowserRouter><HashRouter>兩種組件。當存在服務區來管理動態請求時,須要使用<BrowserRouter>組件,而<HashRouter>被用於靜態網站。react-native

一般,咱們更傾向選擇<BrowserRouter>,但若是你的網站僅用來呈現靜態文件,那麼<HashRouter>將會是一個好選擇。數組

對於咱們的項目,將設將會有服務器的動態支持,所以咱們選擇<BrowserRouter>做爲路由器組件。瀏覽器

歷史(History)

每一個路由器都會建立一個history對象並用其保持追蹤當前location[注1]而且在有變化時對網站進行從新渲染。這個history對象保證了React Router提供的其餘組件的可用性,因此其餘組件必須在router內部渲染。一個React Router組件若是向父級上追溯卻找不到router組件,那麼這個組件將沒法正常工做。服務器

渲染<Router>

路由器組件沒法接受兩個及以上的子元素。基於這種限制的存在,建立一個<App>組件來渲染應用其他部分是一個有效的方法(對於服務端渲染,將應用從router組件中分離也是重要的)。

import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'))

<App>

應用經過<App>組件定義。簡化一下,咱們將應用拆分紅兩個部分。<Header>組件包含網站的導航連接。<Main>組件則呈現其他內容。

// this component will be rendered by our <___Router>
const App = () => (
  <div>
    <Header />
    <Main />
  </div>
)

注意:你能夠按你喜歡的方式對應用佈局,可是將路由與導航拆分出來對於這個入門教程會成家簡單。

路由(Route)

<Route>組件是React Router中主要的結構單元。在任意位置只要匹配了URL的路徑名(pathname)你就能夠建立<Route>元素進行渲染。

路徑(Path)

<Route>接受一個數爲string類型的path,該值路由匹配的路徑名的類型。例如:<Route path='/roster'/>會匹配以/roster[注2]開頭的路徑名。在當前path參數與當前location的路徑相匹配時,路由就會開始渲染React元素。若不匹配,路由不會進行任何操做[注3]。

<Route path='/roster'/>
// 當路徑名爲'/'時, path不匹配
// 當路徑名爲'/roster'或'/roster/2'時, path匹配
// 當你只想匹配'/roster'時,你須要使用"exact"參數
// 則路由僅匹配'/roster'而不會匹配'/roster/2'
<Route exact path='/roster'/>

注意:在匹配路由時,React Router只關注location的路徑名。當URL以下時:

http://www.example.com/my-projects/one?extra=false

React Router去匹配的只是'/my-projects/one'這一部分。

匹配路徑

path-to-regexp包用來決定route元素的path參數與當前location是否匹配。它將路徑字符串編譯成正則表達式,並與當前location的路徑名進行匹配比較。除了上面的例子外,路徑字符串有更多高級的選項,詳見[path-to-regexp文檔]。
當路由地址匹配成功後,會建立一個含有如下屬性的match對象

  • url :與當前location路徑名所匹配部分

  • path :路由的地址

  • isExact :path 是否等於 pathname

  • params :從path-to-regexp獲取的路徑中取出的值都被包含在這個對象中

使用route tester這款工具來對路由與URL進行檢驗。

注意:本例中路由路徑僅支持絕對路徑[注4]。

建立你的路由

能夠在路由器(router)組件中的任意位置建立多個<Route>,但一般咱們會把它們放在同一個位置。使用<Switch>組件來包裹一組<Route>。<Switch>會遍歷自身的子元素(即路由)並對第一個匹配當前路徑的元素進行渲染。

對於本網站,咱們但願匹配一下路徑:

  • / : 主頁

  • /roster : 團體列表

  • /roster/:number :運動員頁面,使用運動員的編號做爲標識

  • /schedule :團隊的賽程表

爲了在應用中能匹配路徑,在建立<Route>元素時必須帶有須要匹配的path做爲參數。

<Switch>
  <Route exact path='/' component={Home}/>
  {/* both /roster and /roster/:number begin with /roster */}
  <Route path='/roster' component={Roster}/>
  <Route path='/schedule' component={Schedule}/>
</Switch>

<Route>是如何渲染的?

當一個路由的path匹配成功後,路由用來肯定渲染結果的參數有三種。只須要提供其中一個便可。

  • component : 一個React組件。當帶有component參數的route匹配成功後,route會返回一個新的元素,其爲component參數所對應的React組件(使用React.createElement建立)。

  • render : 一個返回React element的函數[注5]。當匹配成功後調用該函數。該過程與傳入component參數相似,而且對於行級渲染與須要向元素傳入額外參數的操做會更有用。

  • children : 一個返回React element的函數。與上述兩個參數不一樣,不管route是否匹配當前location,其都會被渲染。

<Route path='/page' component={Page} />
const extraProps = { color: 'red' }
<Route path='/page' render={(props) => (
  <Page {...props} data={extraProps}/>
)}/>
<Route path='/page' children={(props) => (
  props.match
    ? <Page {...props}/>
    : <EmptyPage {...props}/>
)}/>

一般component參數與render參數被更常常地使用。children參數偶爾會被使用,它更經常使用在path沒法匹配時呈現的'空'狀態。在本例中並不會有額外的狀態,因此咱們將使用<Route>的component參數。

經過<Route>渲染的元素會被傳入一些參數。分別是match對象,當前location對象[注6]以及history對象(由router建立)[注7]。

<Main>

如今咱們清楚了根路由的結構,咱們須要實際渲染咱們的路由。對於這個應用,咱們將會在<Main>組件中渲染<Switch>與<Route>,這一過程會將route匹配生成的HTML放在<main>節點中。

import { Switch, Route } from 'react-router-dom'
const Main = () => (
  <main>
    <Switch>
      <Route exact path='/' component={Home}/>
      <Route path='/roster' component={Roster}/>
      <Route path='/schedule' component={Schedule}/>
    </Switch>
  </main>
)

注意:主頁路由包含額外參數。該參數用來保證路由能準確匹配path。

嵌套路由

運動員路由/roster/:number並未包含在上述<Switch>中。它由<Roster>組件負責在路徑包含'/roster'的情形下進行渲染。

在<Roster>組件中,咱們將爲兩種路徑進行渲染:

  • /roster :對應路徑名僅僅是/roster時,所以須要在exact元素上添加exact參數。

  • /roster/:number : 該路由使用一個路由參數來獲取/roster後的路徑名。

    const Roster = () => (
    <Switch>

    <Route exact path='/roster' component={FullRoster}/>
    <Route path='/roster/:number' component={Player}/>

    </Switch>
    )

    組合在相同組件中分享共同前綴的路由是一種有用的方法。這就須要簡化父路由而且提供一個區域來渲染具備相同前綴的通用路由。

例如,<Roster>用來渲染全部以/roster開始的所有路由。

const Roster = () => (
  <div>
    <h2>This is a roster page!</h2>
    <Switch>
      <Route exact path='/roster' component={FullRoster}/>
      <Route path='/roster/:number' component={Player}/>
    </Switch>
  </div>
)

路徑參數

有時路徑名中存在咱們須要獲取的參數。例如,在運動員界面,咱們須要獲取運動員的編號。咱們能夠向route的路徑字符串中添加path參數

如'/roster/:number'中:number這種寫法意味着/roster/後的路徑名將會被獲取並存在match.params.number中。例如,路徑名'/roster/6'會獲取到一個對象:

{ number: '6' } // 注獲取的值是字符串類型的

<Player>組件可使用props.match.params對象來肯定須要被渲染的運動員的數據。

// 返回運動員對象的API
import PlayerAPI from './PlayerAPI'
const Player = (props) => {
  const player = PlayerAPI.get(
    parseInt(props.match.params.number, 10)
  )
  if (!player) {
    return <div>Sorry, but the player was not found</div>
  }
  return (
    <div>
      <h1>{player.name} (#{player.number})</h1>
      <h2>{player.position}</h2>
    </div>
)

你能夠經過閱讀path-to-regexp文檔來了解更多。

除了<Player>組件,咱們的頁面還包含<FullRoster>, <Schedule>以及 <Home>組件。

const FullRoster = () => (
  <div>
    <ul>
      {
        PlayerAPI.all().map(p => (
          <li key={p.number}>
            <Link to={`/roster/${p.number}`}>{p.name}</Link>
          </li>
        ))
      }
    </ul>
  </div>
)
const Schedule = () => (
  <div>
    <ul>
      <li>6/5 @ Evergreens</li>
      <li>6/8 vs Kickers</li>
      <li>6/14 @ United</li>
    </ul>
  </div>
)
const Home = () => (
  <div>
    <h1>Welcome to the Tornadoes Website!</h1>
  </div>
)

Link

如今,咱們應用須要在各個頁面間切換。若是使用錨點元素(就是)實現,在每次點擊時頁面將被從新加載。React Router提供了<Link>組件用來避免這種情況的發生。當你點擊<Link>時,URL會更新,組件會被從新渲染,可是頁面不會從新加載。

import { Link } from 'react-router-dom'
const Header = () => (
  <header>
    <nav>
      <ul>
        <li><Link to='/'>Home</Link></li>
        <li><Link to='/roster'>Roster</Link></li>
        <li><Link to='/schedule'>Schedule</Link></li>
      </ul>
    </nav>
  </header>
)

<Link>使用'to'參數來描述須要定位的頁面。它的值便可是字符串也但是location對象(包含pathname,search,hash與state屬性)。若是其值爲字符牀將會被轉換爲location對象。

<Link to={{ pathname: '/roster/7' }}>Player #7</Link>

注意:本例的link的pathname屬性只能是絕對路徑[注4]。

例子

一個完整的網站例子

獲取路由

但願當下你已準備好深刻構建你本身的網站了。

咱們已經瞭解了構建網站所須要的全部必須組件(<BrowserRouter>, <Route>, 以及 <Link>)。固然,還有一些咱們沒有涉及的組件。所幸React Router擁有優質的文檔,你能夠查看並從中瞭解更多的信息。文檔也提供一系列的例子與源代碼。

註釋:

[1] locations 是一個含有描述URL不一樣部分屬性的對象:

// 一個基本的location對象
{ pathname: '/', search: '', hash: '', key: 'abc123' state: {} }

[2] 你能夠渲染無路徑的<Route>,其將會匹配全部location。此法用於訪問存在上下文中的變量與方法。

[3] 若是你使用children參數,即使在當前location不匹配時route也將進行渲染。

[4] 當須要支持相對路徑的<Route>與<Link>時,你須要多作一些工做。相對<Link>將會比你以前看到的更爲複雜。因其使用了父級的match對象而非當前URL來匹配相對路徑。

[5] 這是一個本質上無狀態的函數組件。內部實現,component參數與render參數的組件是用很大的區別的。使用component參數的組件會使用React.createElement來建立元素,使用render參數的組件則會調用render函數。若是咱們定義一個內聯函數並將其傳給component參數,這將會比使用render參數慢不少。

<Route path='/one' component={One}/>
// React.createElement(props.component)
<Route path='/two' render={() => <Two />}/>
// props.render()

[6] <Route>與<Switch>組件都會帶有location參數。這能讓你使用與實際location不一樣的location去匹配地址。

[7] 能夠傳入staticContext參數,不過這僅在服務端渲染時有用。

相關文章
相關標籤/搜索