react-router v2學(xué)習(xí)筆記

轉(zhuǎn)載自 阮一峰React Router 使用教程
參考
React router 中文文檔
React router 例子

一、基本用法

使用時(shí),路由器Router就是React的一個(gè)組件。

import { Router } from 'react-router';
render(<Router/>, document.getElementById('app'));

Router組件本身只是一個(gè)容器,真正的路由要通過Route組件定義。

import { Router, Route, hashHistory } from 'react-router';

render((
  <Router history={hashHistory}>
    <Route path="/" component={App}/>
  </Router>
), document.getElementById('app'));

上面代碼中,用戶訪問根路由/(比如http://www.example.com/),組件APP就會(huì)加載到document.getElementById('app')
你可能還注意到,Router組件有一個(gè)參數(shù)history,它的值hashHistory表示,路由的切換由URL的hash變化決定,即URL的#部分發(fā)生變化。舉例來說,用戶訪問http://www.example.com/,實(shí)際會(huì)看到的是http://www.example.com/#/
Route組件定義了URL路徑與組件的對(duì)應(yīng)關(guān)系。你可以同時(shí)使用多個(gè)Route組件。

<Router history={hashHistory}>
  <Route path="/" component={App}/>
  <Route path="/repos" component={Repos}/>
  <Route path="/about" component={About}/>
</Router>

上面代碼中,用戶訪問/repos(比如http://localhost:8080/#/repos)時(shí),加載Repos組件;訪問/about(http://localhost:8080/#/about)時(shí),加載About組件。

二、嵌套路由

Route組件還可以嵌套。

<Router history={hashHistory}>
  <Route path="/" component={App}>
    <Route path="/repos" component={Repos}/>
    <Route path="/about" component={About}/>
  </Route>
</Router>

上面代碼中,用戶訪問/repos時(shí),會(huì)先加載App組件,然后在它的內(nèi)部再加載Repos組件。

<App>
  <Repos/>
</App>

App組件要寫成下面的樣子。

export default React.createClass({
  render() {
    return <div>
      {this.props.children}
    </div>
  }
})

上面代碼中,App組件的this.props.children屬性就是子組件。
子路由也可以不寫在Router組件里面,單獨(dú)傳入Router組件的routes屬性。

let routes = <Route path="/" component={App}>
  <Route path="/repos" component={Repos}/>
  <Route path="/about" component={About}/>
</Route>;

<Router routes={routes} history={browserHistory}/>

三、 path 屬性

Route組件的path屬性指定路由的匹配規(guī)則。這個(gè)屬性是可以省略的,這樣的話,不管路徑是否匹配,總是會(huì)加載指定組件。

請看下面的例子。


<Route path="inbox" component={Inbox}>
   <Route path="messages/:id" component={Message} />
</Route>

上面代碼中,當(dāng)用戶訪問/inbox/messages/:id時(shí),會(huì)加載下面的組件。


<Inbox>
  <Message/>
</Inbox>

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


<Route component={Inbox}>
  <Route path="inbox/messages/:id" component={Message} />
</Route>

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


<Inbox>
  <Message/>
</Inbox>

四、通配符

path屬性可以使用通配符。


<Route path="/hello/:name">
// 匹配 /hello/michael
// 匹配 /hello/ryan

<Route path="/hello(/:name)">
// 匹配 /hello
// 匹配 /hello/michael
// 匹配 /hello/ryan

<Route path="/files/*.*">
// 匹配 /files/hello.jpg
// 匹配 /files/hello.html

<Route path="/files/*">
// 匹配 /files/ 
// 匹配 /files/a
// 匹配 /files/a/b

<Route path="/**/*.jpg">
// 匹配 /files/hello.jpg
// 匹配 /files/path/to/file.jpg

通配符的規(guī)則如下。

(1):paramName

:paramName匹配URL的一個(gè)部分,直到遇到下一個(gè)/?#為止。這個(gè)路徑參數(shù)可以通過this.props.params.paramName取出。

(2)()

()表示URL的這個(gè)部分是可選的。

(3)*

*匹配任意字符,直到模式里面的下一個(gè)字符為止。匹配方式是非貪婪模式。

(4) **

** 匹配任意字符,直到下一個(gè)/?#為止。匹配方式是貪婪模式。

path屬性也可以使用相對(duì)路徑(不以/開頭),匹配時(shí)就會(huì)相對(duì)于父組件的路徑,可以參考上一節(jié)的例子。嵌套路由如果想擺脫這個(gè)規(guī)則,可以使用絕對(duì)路由。

路由匹配規(guī)則是從上到下執(zhí)行,一旦發(fā)現(xiàn)匹配,就不再其余的規(guī)則了。


<Route path="/comments" ... />
<Route path="/comments" ... />

上面代碼中,路徑/comments同時(shí)匹配兩個(gè)規(guī)則,第二個(gè)規(guī)則不會(huì)生效。

設(shè)置路徑參數(shù)時(shí),需要特別小心這一點(diǎn)。


<Router>
  <Route path="/:userName/:id" component={UserPage}/>
  <Route path="/about/me" component={About}/>
</Router>

上面代碼中,用戶訪問/about/me時(shí),不會(huì)觸發(fā)第二個(gè)路由規(guī)則,因?yàn)樗鼤?huì)匹配/:userName/:id這個(gè)規(guī)則。因此,帶參數(shù)的路徑一般要寫在路由規(guī)則的底部。

此外,URL的查詢字符串/foo?bar=baz,可以用this.props.location.query.bar獲取。

五、IndexRoute 組件

下面的例子,你會(huì)不會(huì)覺得有一點(diǎn)問題?


<Router>
  <Route path="/" component={App}>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

上面代碼中,訪問根路徑/,不會(huì)加載任何子組件。也就是說,App組件的this.props.children,這時(shí)是undefined

因此,通常會(huì)采用{this.props.children || <Home/>}這樣的寫法。這時(shí),Home明明是AccountsStatements的同級(jí)組件,卻沒有寫在Route中。

IndexRoute就是解決這個(gè)問題,顯式指定Home是根路由的子組件,即指定默認(rèn)情況下加載的子組件。你可以把IndexRoute想象成某個(gè)路徑的index.html


<Router>
  <Route path="/" component={App}>
    <IndexRoute component={Home}/>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

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


<App>
  <Home/>
</App>

這種組件結(jié)構(gòu)就很清晰了:App只包含下級(jí)組件的共有元素,本身的展示內(nèi)容則由Home組件定義。這樣有利于代碼分離,也有利于使用React Router提供的各種API。

注意,IndexRoute組件沒有路徑參數(shù)path

六、Redirect 組件

<Redirect>組件用于路由的跳轉(zhuǎn),即用戶訪問一個(gè)路由,會(huì)自動(dòng)跳轉(zhuǎn)到另一個(gè)路由。


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

現(xiàn)在訪問/inbox/messages/5,會(huì)自動(dòng)跳轉(zhuǎn)到/messages/5

七、IndexRedirect 組件

IndexRedirect組件用于訪問根路由的時(shí)候,將用戶重定向到某個(gè)子組件。


<Route path="/" component={App}>
  <IndexRedirect to="/welcome" />
  <Route path="welcome" component={Welcome} />
  <Route path="about" component={About} />
</Route>

上面代碼中,用戶訪問根路徑時(shí),將自動(dòng)重定向到子組件welcome

八、Link

Link組件用于取代<a>元素,生成一個(gè)鏈接,允許用戶點(diǎn)擊后跳轉(zhuǎn)到另一個(gè)路由。它基本上就是<a>元素的React 版本,可以接收Router的狀態(tài)。


render() {
  return <div>
    <ul role="nav">
      <li><Link to="/about">About</Link></li>
      <li><Link to="/repos">Repos</Link></li>
    </ul>
  </div>
}

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


<Link to="/about" activeStyle={{color: 'red'}}>About</Link>
<Link to="/repos" activeStyle={{color: 'red'}}>Repos</Link>

上面代碼中,當(dāng)前頁面的鏈接會(huì)紅色顯示。

另一種做法是,使用activeClassName指定當(dāng)前路由的Class


<Link to="/about" activeClassName="active">About</Link>
<Link to="/repos" activeClassName="active">Repos</Link>

上面代碼中,當(dāng)前頁面的鏈接的class會(huì)包含active

Router組件之外,導(dǎo)航到路由頁面,可以使用瀏覽器的History API,像下面這樣寫。


import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

九、IndexLink

如果鏈接到根路由/,不要使用Link組件,而要使用IndexLink組件。

這是因?yàn)閷?duì)于根路由來說,activeStyleactiveClassName會(huì)失效,或者說總是生效,因?yàn)?code>/會(huì)匹配任何子路由。而IndexLink組件會(huì)使用路徑的精確匹配。


<IndexLink to="/" activeClassName="active">
  Home
</IndexLink>

上面代碼中,根路由只會(huì)在精確匹配時(shí),才具有activeClassName

另一種方法是使用Link組件的onlyActiveOnIndex屬性,也能達(dá)到同樣效果。


<Link to="/" activeClassName="active" onlyActiveOnIndex={true}>
  Home
</Link>

實(shí)際上,IndexLink就是對(duì)Link組件的onlyActiveOnIndex屬性的包裝。

十、history 屬性

Router組件的history屬性,用來監(jiān)聽瀏覽器地址欄的變化,并將URL解析成一個(gè)地址對(duì)象,供 React Router 匹配。

history屬性,一共可以設(shè)置三種值。

  • browserHistory
  • hashHistory
  • createMemoryHistory

如果設(shè)為hashHistory,路由將通過URL的hash部分(#)切換,URL的形式類似example.com/#/some/path


import { hashHistory } from 'react-router'

render(
  <Router history={hashHistory} routes={routes} />,
  document.getElementById('app')
)

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


import { browserHistory } from 'react-router'

render(
  <Router history={browserHistory} routes={routes} />,
  document.getElementById('app')
)

但是,這種情況需要對(duì)服務(wù)器改造。否則用戶直接向服務(wù)器請求某個(gè)子路由,會(huì)顯示網(wǎng)頁找不到的404錯(cuò)誤。

如果開發(fā)服務(wù)器使用的是webpack-dev-server,加上--history-api-fallback參數(shù)就可以了。


$ webpack-dev-server --inline --content-base . --history-api-fallback

createMemoryHistory主要用于服務(wù)器渲染。它創(chuàng)建一個(gè)內(nèi)存中的history對(duì)象,不與瀏覽器URL互動(dòng)。


const history = createMemoryHistory(location)

十一、表單處理

Link組件用于正常的用戶點(diǎn)擊跳轉(zhuǎn),但是有時(shí)還需要表單跳轉(zhuǎn)、點(diǎn)擊按鈕跳轉(zhuǎn)等操作。這些情況怎么跟React Router對(duì)接呢?
下面是一個(gè)表單。

<form onSubmit={this.handleSubmit}>
  <input type="text" placeholder="userName"/>
  <input type="text" placeholder="repo"/>
  <button type="submit">Go</button>
</form>

第一種方法是使用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對(duì)象。

export default React.createClass({

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

  handleSubmit(event) {
    // ...
    this.context.router.push(path)
  },
})

十二、路由的鉤子

每個(gè)路由都有Enter和Leave鉤子,用戶進(jìn)入或離開該路由時(shí)觸發(fā)。

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

上面的代碼中,如果用戶離開/messages/:id,進(jìn)入/about時(shí),會(huì)依次觸發(fā)以下的鉤子。

/messages/:id的onLeave
/inbox的onLeave
/about的onEnter

下面是一個(gè)例子,使用onEnter鉤子替代<Redirect>組件。

<Route path="inbox" component={Inbox}>
  <Route
    path="messages/:id"
    onEnter={
      ({params}, replace) => replace(`/messages/${params.id}`)
    } 
  />
</Route>

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

const requireAuth = (nextState, replace) => {
    if (!auth.isAdmin()) {
        // Redirect to Home page if not an Admin
        replace({ pathname: '/' })
    }
}
export const AdminRoutes = () => {
  return (
     <Route path="/admin" component={Admin} onEnter={requireAuth} />
  )
}

下面是一個(gè)高級(jí)應(yīng)用,當(dāng)用戶離開一個(gè)路徑的時(shí)候,跳出一個(gè)提示框,要求用戶確認(rèn)是否離開。

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

    routerWillLeave(nextLocation) {
      // 返回 false 會(huì)繼續(xù)停留當(dāng)前頁面,
      // 否則,返回一個(gè)字符串,會(huì)顯示給用戶,讓其自己決定
      if (!this.state.isSaved)
        return '確認(rèn)要離開?';
    },
  })
)

上面代碼中,setRouteLeaveHook方法為Leave鉤子指定routerWillLeave函數(shù)。該方法如果返回false,將阻止路由的切換,否則就返回一個(gè)字符串,提示用戶決定是否要切換。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,581評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,922評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,096評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,374評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,591評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,789評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,322評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,554評(píng)論 2 379

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