React-Router看這裏

前言

從事前端工做的第一個項目即是和同事協做構建一個大型SPA——企業後端管理平臺,應用架構相似於企業微信·管理平臺。使用React技術棧來完成前端開發,React-Router成爲了必不可少的利器,無刷新的組件切換讓用戶體驗更佳。
看了許多學習文檔,也在工做中進行了實踐,此文用於總結React-Router。html

解決的問題

使用路由能夠自如流暢得嚮應用中添加,切換組件,並保持頁面與URL間的同步,這在SPA中應用普遍。使用React-Router咱們不須要手動尋找須要渲染的組件,無需編寫複雜的邏輯,只需完成相應的路由配置,其它交給路由來解決。前端

用例

import React from 'react'
import { render } from 'react-dom'

// 首先咱們須要導入一些組件...
import { Router, Route, Link } from 'react-router'

const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        {/* 把 <a> 變成 <Link> */}
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inbox">Inbox</Link></li>
        </ul>

        {/*
          接着用 `this.props.children` 替換 `<Child>`
          router 會幫咱們找到這個 children
          childern所對應的組件可見於路由規則的嵌套關係中
          在此例中爲About或Inbox或undefined
        */}
        {this.props.children}
      </div>
    )
  }
})

// 最後,咱們用一些 <Route> 來渲染 <Router>。
// Router是路由容器,route爲真實路由,路由之間能夠進行嵌套。
// 這些就是路由提供的咱們想要的東西。
React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="messages/:id" component={Message} />
    </Route>
  </Router>
), document.body)
複製代碼

Router是一個React組件,做爲路由容器來使用,Route中所包含的即是路由的規則。在使用時咱們可使用頁面的層次關係來搭建路由規則。
在Route中有兩個重要的屬性:react

  • path屬性置頂路由的匹配規則,將顯示在瀏覽器的地址欄在域名後面。path屬性省略時,老是會加載這個路由節點的指定組件。
  • component屬性指定URL匹配此路由規則時,所要掛載的組件。

經過上面的配置,此應用會以以下方式渲染URL:算法

URL 組件
/ App
/about App -> About
/inbox App -> Inbox
/messages/[someid] App -> Message

路徑語法

路由路徑是匹配一個(或一部分)URL的一個字符串模式。
你是否觀察到路由規則的第三條有些不太同樣,它的路徑是path="messages/:id",並未確切的指定一個路由地址。redux

在React-Router中有幾種特殊的匹配規則:後端

  • :paramName 匹配一段位於/?#以後的URL。
  • () 在其內部的規則被是可選的,匹配時可忽略。
  • * 匹配任意字符(非貪婪的)知道下一個字符或者整個URL的末尾。
<Route path="/hello/:name">         // 匹配 /hello/michael 和 /hello/ryan
  <Route path="/hello(/:name)">       // 匹配 /hello, /hello/michael 和 /hello/ryan
  <Route path="/files/*.*">           // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
複製代碼

咱們可使用如用例的相對地址,也可使用以下的絕對地址,當路由規則嵌套很深時,這大有用處,能夠簡化咱們的路由規則。瀏覽器

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Index} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        {/* 使用 /messages/:id 替換 messages/:id */}
        <Route path="/messages/:id" component={Message} />
      </Route>
    </Route>
  </Router>
), document.body)
複製代碼

經過上面的配置,此應用會以以下方式渲染URL:服務器

URL 組件
/ App -> Index
/about App -> About
/inbox App -> Inbox
/messages/[someid] App -> Inbox -> Message

提醒: 絕對路徑在動態路由中沒法使用。微信

History

React-Router是創建在history之上的。history知道如何去監聽瀏覽器地址欄的變化, 並解析這個URL轉化爲location對象, 而後router使用它匹配到路由,最後正確地渲染對應的組件。react-router

經常使用的history有三種形式:

  • browserHistory
  • hashHistory
  • createMemoryHistory

使用方法是將history傳入中:

// JavaScript 模塊導入(譯者注:ES6 形式)
import { browserHistory } from 'react-router'

render(
  <Router history={browserHistory} />, document.getElementById('app') ) 複製代碼

React-Router推薦使用BrosbrowserHistoryerHistory,它使用瀏覽器中的History API來處理URL。服務器須要進行相應配合,須要服務器做好處理URL的準備。
hashHistory 使用URK中的hash(#)部分建立形如 example.com/#/some/path 的路由。 createMemoryHistory 用做SSR(服務端渲染),此處不做深刻了解。

IndexRoute 和 Redirect

IndexRoute

當咱們訪問的連接爲www.demo.html/時,頁面將加載App組件。可是此時,App的render中的this.props.children爲undefined,由於咱們的路由並不匹配App的子組件。此時,咱們可使用IndexRoute來設置一個默認頁面。改造路由以下:

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Index}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="messages/:id" component={Message} />
    </Route>
  </Router>
), document.body)
複製代碼

此時,App的render中的this.props.children將會是組件。

Redirect

思考一個問題,若是在地址欄輸入了路由規則中不匹配的URL,如www.demo.html/#/noroute,會發生什麼?頁面會一片空白,空空如也。
咱們有這樣一個需求,訪問www.demo.html/#/inbox時,我想讓地址跳轉到www.demo.html/#/about,渲染About組件,或是修正上面的空白錯誤,跳轉到About組件。
要解決這樣的問題,咱們可使用Redirect來進行重定向。

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Index}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="messages/:id" component={Message} />
      {/* 跳轉 /inbox 到 /about */}
      <Redirect from="inbox" to="about" />
    </Route>
  </Router>
), document.body)
複製代碼

路由跳轉

路由跳轉能夠分爲內部跳轉和外部跳轉,內部跳轉使用Link來完成,原理如a標籤通常。組件外跳轉可以使用push方法來完成。

Link

Link組件用於取代a標籤,生成一個連接,容許用戶點擊後跳轉到另外一個路由,如用例中的使用方法。activeStyle屬性能夠爲當前路由添加樣式。或者使用activeClassName爲當前路由添加class。

<Link to="/about" activeStyle={{color: 'red'}}>About</Link>
<Link to="/inbox" activeStyle={{color: 'green'}}>Inbox</Link>
複製代碼

push

須要在提交表單,點擊按鈕時跳轉,咱們可使用push方法完成。

// somewhere like a redux/flux action file:
import { browserHistory } from 'react-router'
browserHistory.push('/some/path')
複製代碼

IndexLink

若是你在這個app中使用<Link to="/">Home</Link>,它會一直處於激活狀態,activeStyle和activeClassName會失效,或者說老是生效。由於全部的路由規則的開頭都是 / 。這確實是個問題,由於咱們僅僅但願在Home被渲染後,激活並連接到它。若是須要在Home路由被渲染後才激活的指向 / 的連接,即實現精確匹配,/只匹配根路由,可使用 <IndexLink to="/">Home</IndexLink>

或者使用Link組件的onlyActiveOnIndex屬性,也能夠達到一樣的效果。

<Link to="/" activeClassName="active" onlyActiveOnIndex={true}>
  Home
</Link>
複製代碼

動態路由

React-Router(Versoin 4)實現了動態路由。

對於大型應用來講,一個首當其衝的問題就是所需加載的JavaScript的大小。程序應當只加載當前渲染頁所需的JavaScript。有些開發者將這種方式稱之爲「代碼分拆」 —— 將全部的代碼分拆成多個小包,在用戶瀏覽過程當中按需加載。React-Router 裏的路徑匹配以及組件加載都是異步完成的,不只容許你延遲加載組件,而且能夠延遲加載路由配置。Route能夠定義 getChildRoutes,getIndexRoute 和 getComponents 這幾個函數。它們都是異步執行,而且只有在須要時才被調用。咱們將這種方式稱之爲 「逐漸匹配」。 React-Router 會逐漸的匹配 URL 並只加載該URL對應頁面所需的路徑配置和組件。

const CourseRoute = {
  path: 'course/:courseId',

  getChildRoutes(location, callback) {
    require.ensure([], function (require) {
      callback(null, [
        require('./routes/Announcements'),
        require('./routes/Assignments'),
        require('./routes/Grades'),
      ])
    })
  },

  getIndexRoute(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Index'))
    })
  },

  getComponents(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Course'))
    })
  }
}
複製代碼

Hook生命週期鉤子

Route能夠定義onEnter和onLeave兩個hook,用於捕捉進入路由和離開路由的時間點,執行一些操做,如完成權限驗證。

在路由跳轉過程當中,onLeave hook 會在全部將離開的路由中觸發,從最下層的子路由開始直到最外層父路由結束。而後onEnter hook會從最外層的父路由開始直到最下層子路由結束。和事件捕獲與事件冒泡的機制相同。

<Router>
    <Route path="about" component={About} onEnter={/*dosomething*/} /> <Route path="inbox" component={Inbox}> <Redirect from="messages/:id" to="/messages/:id" onLeave={/*dosomething*/} /> </Route> </Router> 複製代碼

上面的代碼中,若是用戶離開/messages/:id,進入/about時,會依次觸發如下的鉤子。

/messages/:id的onLeave
/inbox的onLeave
/about的onEnter
複製代碼

routerWillLeave是離開當前路由時的生命週期鉤子。該方法若是返回false,將阻止路由的切換,不然就返回一個字符串,在離開route前提示用戶進行確認。

import { Lifecycle } from 'react-router'

const Home = React.createClass({

  // 假設 Home 是一個 route 組件,它可能會使用
  // Lifecycle mixin 去得到一個 routerWillLeave 方法。
  mixins: [ Lifecycle ],

  routerWillLeave(nextLocation) {
    if (!this.state.isSaved)
      return 'Your work is not saved! Are you sure you want to leave?'
  },

  // ...

})
複製代碼

Corner

  • 路由算法會根據定義的順序自頂向下匹配路由,匹配第一個符合規則的路由。
  • 能夠經過this.props.params.id來獲取 /:id 中的id。
  • URL的查詢字符串/foo?bar=baz,能夠用this.props.location.query.bar獲取。
相關文章
相關標籤/搜索