(轉(zhuǎn)載)react router學(xué)習(xí)

作者: 阮一峰

本文轉(zhuǎn)載于阮一峰個人網(wǎng)站

你會發(fā)現(xiàn)员帮,它不是一個庫或粮,也不是一個框架,而是一個龐大的體系捞高。想要發(fā)揮它的威力氯材,整個技術(shù)棧都要配合它改造。你要學(xué)習(xí)一整套解決方案硝岗,從后端到前端浓体,都是全新的做法。

舉例來說辈讶,React 不使用 HTML命浴,而使用 JSX 。它打算拋棄 DOM,要求開發(fā)者不要使用任何 DOM 方法生闲。它甚至還拋棄了 SQL 媳溺,自己發(fā)明了一套查詢語言 GraphQL 。當(dāng)然碍讯,這些你都可以不用悬蔽,React 照樣運行,但是就發(fā)揮不出它的最大威力捉兴。
這樣說吧蝎困,你只要用了 React,就會發(fā)現(xiàn)合理的選擇就是倍啥,采用它的整個技術(shù)棧禾乘。
本文介紹 React 體系的一個重要部分:路由庫React-Router。它是官方維護的虽缕,事實上也是唯一可選的路由庫始藕。它通過管理 URL,實現(xiàn)組件的切換和狀態(tài)的變化氮趋,開發(fā)復(fù)雜的應(yīng)用幾乎肯定會用到伍派。

本文針對初學(xué)者,盡量寫得簡潔易懂剩胁。預(yù)備知識是 React 的基本用法诉植,可以參考我寫的《React 入門實例教程》。
另外昵观,我沒有準(zhǔn)備示例庫倍踪,因為官方的示例庫非常棒,由淺入深索昂,分成14步建车,每一步都有詳細(xì)的代碼解釋。我強烈建議你先跟著做一遍椒惨,然后再看下面的API講解缤至。
([說明] 本文寫作時,React-router 是 2.x 版康谆,本文的內(nèi)容只適合這個版本领斥,與最新的 4.x 版不兼容。目前沃暗,官方同時維護 2.x 和 4.x 兩個版本月洛,所以前者依然可以用在項目中。2017年3月)
一孽锥、基本用法
React Router 安裝命令如下嚼黔。

$ npm install -S react-router

使用時细层,路由器Router就是React的一個組件。

render(, document.getElementById('app'));```
Router組件本身只是一個容器唬涧,真正的路由要通過Route組件定義疫赎。

```import { Router, Route, hashHistory } from 'react-router';render(( 
), document.getElementById('app'));```
上面代碼中,用戶訪問根路由/(比如http://www.example.com/)碎节,組件APP就會加載到document.getElementById('app')捧搞。
你可能還注意到,Router組件有一個參數(shù)history狮荔,它的值hashHistory表示胎撇,路由的切換由URL的hash變化決定,即URL的#部分發(fā)生變化殖氏。舉例來說晚树,用戶訪問http://www.example.com/,實際會看到的是http://www.example.com/#/受葛。
Route組件定義了URL路徑與組件的對應(yīng)關(guān)系。你可以同時使用多個Route組件偎谁。
用戶訪問/repos(比如http://localhost:8080/#/repos)時总滩,加載Repos組件;訪問/about(http://localhost:8080/#/about)時巡雨,加載About組件闰渔。
二、嵌套路由
Route組件還可以嵌套铐望。
用戶訪問/repos時冈涧,會先加載App組件,然后在它的內(nèi)部再加載Repos組件
App組件要寫成下面的樣子正蛙。

```export default React.createClass({
  render() {
    return 

      {this.props.children}
    

  }
})```
上面代碼中督弓,App組件的this.props.children屬性就是子組件。
子路由也可以不寫在Router組件里面乒验,單獨傳入Router組件的routes屬性愚隧。

let routes = 
  
  
;


三、 path 屬性
Route組件的path屬性指定路由的匹配規(guī)則锻全。這個屬性是可以省略的狂塘,這樣的話,不管路徑是否匹配鳄厌,總是會加載指定組件荞胡。
請看下面的例子。


   

上面代碼中了嚎,當(dāng)用戶訪問/inbox/messages/:id時泪漂,會加載下面的組件廊营。


  

如果省略外層Route的path參數(shù),寫成下面的樣子窖梁。


  

現(xiàn)在用戶訪問/inbox/messages/:id時赘风,組件加載還是原來的樣子。


  

四纵刘、通配符
path屬性可以使用通配符邀窃。


// 匹配 /hello/michael
// 匹配 /hello/ryan


// 匹配 /hello
// 匹配 /hello/michael
// 匹配 /hello/ryan


// 匹配 /files/hello.jpg
// 匹配 /files/hello.html


// 匹配 /files/ 
// 匹配 /files/a
// 匹配 /files/a/b


// 匹配 /files/hello.jpg
// 匹配 /files/path/to/file.jpg
通配符的規(guī)則如下。
(1):paramName
:paramName匹配URL的一個部分假哎,直到遇到下一個/瞬捕、?、#為止舵抹。這個路徑參數(shù)可以通過this.props.params.paramName取出肪虎。
(2)()
()表示URL的這個部分是可選的。
(3)*
*匹配任意字符惧蛹,直到模式里面的下一個字符為止扇救。匹配方式是非貪婪模式。
(4) **
** 匹配任意字符香嗓,直到下一個/迅腔、?、#為止靠娱。匹配方式是貪婪模式沧烈。
path屬性也可以使用相對路徑(不以/開頭),匹配時就會相對于父組件的路徑像云,可以參考上一節(jié)的例子锌雀。嵌套路由如果想擺脫這個規(guī)則,可以使用絕對路由迅诬。
路由匹配規(guī)則是從上到下執(zhí)行腋逆,一旦發(fā)現(xiàn)匹配,就不再其余的規(guī)則了侈贷。



上面代碼中闲礼,路徑/comments同時匹配兩個規(guī)則,第二個規(guī)則不會生效铐维。
設(shè)置路徑參數(shù)時柬泽,需要特別小心這一點。


  
  

上面代碼中嫁蛇,用戶訪問/about/me時锨并,不會觸發(fā)第二個路由規(guī)則,因為它會匹配/:userName/:id這個規(guī)則睬棚。因此第煮,帶參數(shù)的路徑一般要寫在路由規(guī)則的底部解幼。
此外,URL的查詢字符串/foo?bar=baz包警,可以用this.props.location.query.bar獲取撵摆。
五、IndexRoute 組件
下面的例子害晦,你會不會覺得有一點問題特铝?


  
    
    
  

上面代碼中,訪問根路徑/壹瘟,不會加載任何子組件鲫剿。也就是說,App組件的this.props.children稻轨,這時是undefined灵莲。
因此,通常會采用{this.props.children || }這樣的寫法殴俱。這時政冻,Home明明是Accounts和Statements的同級組件,卻沒有寫在Route中线欲。
IndexRoute就是解決這個問題明场,顯式指定Home是根路由的子組件,即指定默認(rèn)情況下加載的子組件询筏。你可以把IndexRoute想象成某個路徑的index.html榕堰。


  
    
    
    
  

現(xiàn)在竖慧,用戶訪問/的時候嫌套,加載的組件結(jié)構(gòu)如下。


  

這種組件結(jié)構(gòu)就很清晰了:App只包含下級組件的共有元素圾旨,本身的展示內(nèi)容則由Home組件定義踱讨。這樣有利于代碼分離,也有利于使用React Router提供的各種API砍的。
注意痹筛,IndexRoute組件沒有路徑參數(shù)path。
六廓鞠、Redirect 組件
組件用于路由的跳轉(zhuǎn)帚稠,即用戶訪問一個路由,會自動跳轉(zhuǎn)到另一個路由床佳。


  {/* 從 /inbox/messages/:id 跳轉(zhuǎn)到 /messages/:id */}
  <Redirect from="messages/:id" to="/messages/:id" />

現(xiàn)在訪問/inbox/messages/5滋早,會自動跳轉(zhuǎn)到/messages/5。
七砌们、IndexRedirect 組件
IndexRedirect組件用于訪問根路由的時候杆麸,將用戶重定向到某個子組件搁进。


  <IndexRedirect to="/welcome" />
  
  

上面代碼中,用戶訪問根路徑時昔头,將自動重定向到子組件welcome饼问。
八、Link
Link組件用于取代元素揭斧,生成一個鏈接莱革,允許用戶點擊后跳轉(zhuǎn)到另一個路由。它基本上就是元素的React 版本未蝌,可以接收Router的狀態(tài)驮吱。

render() {
  return 

    

      
About

      
Repos

    

  

}
如果希望當(dāng)前的路由與其他路由有不同樣式,這時可以使用Link組件的activeStyle屬性萧吠。

About
Repos
上面代碼中左冬,當(dāng)前頁面的鏈接會紅色顯示。
另一種做法是纸型,使用activeClassName指定當(dāng)前路由的Class拇砰。

About
Repos
上面代碼中,當(dāng)前頁面的鏈接的class會包含active狰腌。
在Router組件之外除破,導(dǎo)航到路由頁面,可以使用瀏覽器的History API琼腔,像下面這樣寫瑰枫。

import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
九、IndexLink
如果鏈接到根路由/丹莲,不要使用Link組件光坝,而要使用IndexLink組件。
這是因為對于根路由來說甥材,activeStyle和activeClassName會失效盯另,或者說總是生效,因為/會匹配任何子路由洲赵。而IndexLink組件會使用路徑的精確匹配鸳惯。


  Home

上面代碼中,根路由只會在精確匹配時叠萍,才具有activeClassName芝发。
另一種方法是使用Link組件的onlyActiveOnIndex屬性,也能達(dá)到同樣效果苛谷。


  Home

實際上辅鲸,IndexLink就是對Link組件的onlyActiveOnIndex屬性的包裝。
十抄腔、histroy 屬性
Router組件的history屬性瓢湃,用來監(jiān)聽瀏覽器地址欄的變化理张,并將URL解析成一個地址對象,供 React Router 匹配绵患。
history屬性雾叭,一共可以設(shè)置三種值。
browserHistory
hashHistory
createMemoryHistory
如果設(shè)為hashHistory落蝙,路由將通過URL的hash部分(#)切換织狐,URL的形式類似example.com/#/some/path。

import { hashHistory } from 'react-router'

render(
  ,
  document.getElementById('app')
)
如果設(shè)為browserHistory筏勒,瀏覽器的路由就不再通過Hash完成了移迫,而顯示正常的路徑example.com/some/path,背后調(diào)用的是瀏覽器的History API管行。

import { browserHistory } from 'react-router'

render(
  ,
  document.getElementById('app')
)
但是厨埋,這種情況需要對服務(wù)器改造。否則用戶直接向服務(wù)器請求某個子路由捐顷,會顯示網(wǎng)頁找不到的404錯誤荡陷。
如果開發(fā)服務(wù)器使用的是webpack-dev-server,加上--history-api-fallback參數(shù)就可以了迅涮。

$ webpack-dev-server --inline --content-base . --history-api-fallback
createMemoryHistory主要用于服務(wù)器渲染废赞。它創(chuàng)建一個內(nèi)存中的history對象,不與瀏覽器URL互動叮姑。

const history = createMemoryHistory(location)
十一唉地、表單處理
Link組件用于正常的用戶點擊跳轉(zhuǎn),但是有時還需要表單跳轉(zhuǎn)传透、點擊按鈕跳轉(zhuǎn)等操作耘沼。這些情況怎么跟React Router對接呢?
下面是一個表單旷祸。


  
  
  Go

第一種方法是使用browserHistory.push

import { browserHistory } from 'react-router'

// ...
  handleSubmit(event) {
    event.preventDefault()
    const userName = event.target.elements[0].value
    const repo = event.target.elements[1].value
    const path = `/repos/${userName}/${repo}`
    browserHistory.push(path)
  },
第二種方法是使用context對象耕拷。

export default React.createClass({

  // ask for `router` from context
  contextTypes: {
    router: React.PropTypes.object
  },

  handleSubmit(event) {
    // ...
    this.context.router.push(path)
  },
})
十二讼昆、路由的鉤子
每個路由都有Enter和Leave鉤子托享,用戶進入或離開該路由時觸發(fā)。


<Route path="inbox" component={Inbox}>
  <Redirect from="messages/:id" to="/messages/:id" />

上面的代碼中浸赫,如果用戶離開/messages/:id闰围,進入/about時,會依次觸發(fā)以下的鉤子既峡。
/messages/:id的onLeave
/inbox的onLeave
/about的onEnter
下面是一個例子羡榴,使用onEnter鉤子替代組件。


   replace(`/messages/${params.id}`)
    } 
  />

onEnter鉤子還可以用來做認(rèn)證运敢。

const requireAuth = (nextState, replace) => {
    if (!auth.isAdmin()) {
        // Redirect to Home page if not an Admin
        replace({ pathname: '/' })
    }
}
export const AdminRoutes = () => {
  return (
     
  )
}
下面是一個高級應(yīng)用校仑,當(dāng)用戶離開一個路徑的時候忠售,跳出一個提示框,要求用戶確認(rèn)是否離開迄沫。

const Home = withRouter(
  React.createClass({
    componentDidMount() {
      this.props.router.setRouteLeaveHook(
        this.props.route, 
        this.routerWillLeave
      )
    },

    routerWillLeave(nextLocation) {
      // 返回 false 會繼續(xù)停留當(dāng)前頁面稻扬,
      // 否則,返回一個字符串羊瘩,會顯示給用戶泰佳,讓其自己決定
      if (!this.state.isSaved)
        return '確認(rèn)要離開?';
    },
  })
)
上面代碼中尘吗,setRouteLeaveHook方法為Leave鉤子指定routerWillLeave函數(shù)逝她。該方法如果返回false,將阻止路由的切換睬捶,否則就返回一個字符串黔宛,提示用戶決定是否要切換。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末擒贸,一起剝皮案震驚了整個濱河市宁昭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酗宋,老刑警劉巖积仗,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜕猫,居然都是意外死亡寂曹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門回右,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隆圆,“玉大人,你說我怎么就攤上這事翔烁∶煅酰” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵蹬屹,是天一觀的道長侣背。 經(jīng)常有香客問我,道長慨默,這世上最難降的妖魔是什么贩耐? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮厦取,結(jié)果婚禮上潮太,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好铡买,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布更鲁。 她就那樣靜靜地躺著,像睡著了一般奇钞。 火紅的嫁衣襯著肌膚如雪岁经。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天蛇券,我揣著相機與錄音缀壤,去河邊找鬼。 笑死纠亚,一個胖子當(dāng)著我的面吹牛昵骤,可吹牛的內(nèi)容都是我干的蒋院。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼褐桌!你這毒婦竟也來了伤溉?” 一聲冷哼從身側(cè)響起聘裁,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤艺挪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鸿染,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體指蚜,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年涨椒,在試婚紗的時候發(fā)現(xiàn)自己被綠了摊鸡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚕冬,死狀恐怖免猾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情囤热,我是刑警寧澤猎提,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站旁蔼,受9級特大地震影響锨苏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牌芋,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一蚓炬、第九天 我趴在偏房一處隱蔽的房頂上張望松逊。 院中可真熱鬧躺屁,春花似錦、人聲如沸经宏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至耐亏,卻和暖如春徊都,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背广辰。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工暇矫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人择吊。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓李根,卻偏偏與公主長得像,于是被迫代替她去往敵國和親几睛。 傳聞我的和親對象是個殘疾皇子房轿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,180評論 25 707
  • 版權(quán)聲明:本文為博主原創(chuàng)文章囱持,未經(jīng)博主允許不得轉(zhuǎn)載。 PS:轉(zhuǎn)載請注明出處作者:TigerChain地址:http...
    TigerChain閱讀 22,997評論 3 49
  • 前言 總括: 本文采用react+redux+react-router+less+es6+webpack,以實現(xiàn)一...
    秦至閱讀 4,278評論 3 56
  • 會喝酒的人 把酒深進去 不會喝酒的人 讓酒淺出來
    甘萊閱讀 163評論 0 0
  • 排序就是將一組對象按照某種邏輯順序重新排列的過程焕济。比如纷妆,信用卡賬單中的交易是按照日期排序的——這種排序很可能使用了...
    AllenChen666閱讀 651評論 0 1