React-Router v4 學習

React-Router v4

1. 設計理念

React-Router v4 的核心思想是 “動態(tài)路由”固阁,它是相對于 “靜態(tài)路由” 提出的設計概念铃诬。

何為靜態(tài)路由蜕琴?在服務器端所有的路由都是在設計過程已經規(guī)劃完成。在許多客戶端框架中也遵循了上述思路跨晴,即在頁面渲染之前先規(guī)劃好路由,將路由導入到頂層的模塊中描扯。但這種理念在 React-Router 中不再適用倡怎,作者希望在學習 React-Router 之前放棄 “靜態(tài)路由” 設計思路。

1.1. 動態(tài)路由

在 React-Router 中的所有設置都是組件奸晴,因此路由是隨著應用的渲染被同時計算得到冤馏,而不是獨立于應用之外。為了解釋這個理念我們首先學習 React-Router 在應用中的使用,

  • 將 React-Router 的組件聲明到應用頂層:

    import { BrowserRouter } from 'react-router-dom'
    
    ReactDOM.render((
      <BrowserRouter>
        <App/>
      </BrowserRouter>
    ), el)
    
  • 使用 link 組件指向相應的位置:

    const App = () => (
      <div>
        <nav>
          <Link to="/dashboard">Dashboard</Link>
        </nav>
      </div>
    )
    
  • 最后利用 Route 組件關聯(lián)位置與顯示 UI:

    const App = () => (
      <div>
        <nav>
          <Link to="/dashboard">Dashboard</Link>
        </nav>
        <div>
          <Route path="/dashboard" component={Dashboard}/>
        </div>
      </div>
    )
    

如果用戶訪問 /dashboard 寄啼, Route 組件會將路由形式的對象 {match, location, history} 作為屬性參數傳給<Dashboard {...props}> 組件進行渲染逮光,否則不會渲染代箭。

到此為止,似乎與 “靜態(tài)路由” 的概念沒有區(qū)別涕刚,但是不要忘記 Route 是一個組件嗡综,這就意味著 Route 組件是否渲染和其中的 pathcomponent 都可以由父組件的狀態(tài)或屬性進行設置。

1.2. 嵌套路由

在動態(tài)路由中配置嵌套路由就好像 div 標簽中嵌套一個 div 杜漠, 因為 Route 就是一個組件標簽极景。

const App = () => (
  <BrowserRouter>
    {/* 這里是 div 標簽*/}
    <div>
      {/* 這里是 Route 組件標簽*/}
      <Route path="/tacos" component={Tacos}/>
    </div>
  </BrowserRouter>
)

// 當 url 匹配 /tacos 時, 該組件被渲染
const Tacos  = ({ match }) => (
  // 這是嵌套的 div 標簽
  <div>
    {/* 這是嵌套的 Route 組件標簽,
        match.url 用以實現相對路徑 */}
    <Route
      path={match.url + '/carnitas'}
      component={Carnitas}
    />
  </div>
)

1.3. 響應式路由

利用動態(tài)路由理念很容易在 React-Native 中實現響應式的布局驾茴。

const App = () => (
  <AppLayout>
    <Route path="/invoices" component={Invoices}/>
  </AppLayout>
)

const Invoices = () => (
  <Layout>

    {/* always show the nav */}
    <InvoicesNav/>

    <Media query={PRETTY_SMALL}>
      {screenIsSmall => screenIsSmall
        // small screen has no redirect
        ? <Switch>
            <Route exact path="/invoices/dashboard" component={Dashboard}/>
            <Route path="/invoices/:id" component={Invoice}/>
          </Switch>
        // large screen does!
        : <Switch>
            <Route exact path="/invoices/dashboard" component={Dashboard}/>
            <Route path="/invoices/:id" component={Invoice}/>
            <Redirect from="/invoices" to="/invoices/dashboard"/>
          </Switch>
      }
    </Media>
  </Layout>
)

2. 快速入門

首先給出官方的基礎案例:

import React 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>
)

// 首頁組件

const BasicExample = () => (
  <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 BasicExample

隨后分析上例給出的三個 React-Router 組件盼樟,分別是 <BrowserRouter><Route>沟涨, <Link> 恤批。

2.1. <BrowserRouter>

這是一個路由管理器,使用 HTML5 的 history API (pushState, replaceState, popState) 同步訪問 URL 和組件裹赴。具體聲明如下:

import { BrowserRouter } from 'react-router-dom'

<BrowserRouter
  basename={optionalString}
  forceRefresh={optionalBool}
  getUserConfirmation={optionalFunc}
  keyLength={optionalNumber}
>
  <App/>
</BrowserRouter>

2.1.1. basename: string

應用的根目錄喜庞,如果你的應用只是整個服務中的子服務,僅適應于某個子目錄棋返,那么就應該設置這個參數延都。

<BrowserRouter basename="/calendar"/>
<Link to="/today"/> // renders <a href="/calendar/today">

2.2. <Link>

該組件用以聲明應用的鏈接(導航)

import { Link } from 'react-router-dom'

<Link to="/about">About</Link>

2.2.1. to: string || object

定義需要導航的路徑。

<Link to="/courses"/>

<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }
}}/>

2.2.2. replace: bool

如果該選項設置為 true睛竣,當你點擊鏈接時晰房,會在 history 棧中取代當前的狀態(tài)路徑而不是添加一個新狀態(tài)路徑。

2.3. <Route>

這是 React-Router 中最重要的組件了射沟,當請求的狀態(tài)路徑與該組件給出的 path 一致時殊者,會渲染所對應的 UI 組件。

import { BrowserRouter as Router, Route } from 'react-router-dom'

<Router>
  <div>
    <Route exact path="/" component={Home}/>
    <Route path="/news" component={NewsFeed}/>
  </div>
</Router>

在 <Route> 組件中有三種渲染方式:

  • <Route component>
  • <Route render>
  • <Route children>

每一種方式都會傳入相同形式的路由屬性 —— {match, location, history} 验夯。

2.3.1. component

使用 component 渲染方式時猖吴,React 會自動將所對應的組件轉化為 React 組件,因此如果所對應組件是內聯(lián)函數形式挥转,請使用 render 或 children 渲染方式海蔽,避免每次都生成新的 React 組件。

<Route path="/user/:username" component={User}/>

2.3.2. render: func

該方式取代了React 生成組件的過程绑谣,而是直接執(zhí)行一個函數党窜,此外還經常用于路由的打包。

// 直接執(zhí)行函數
<Route path="/home" render={() => <div>Home</div>}/>

// 打包路由
const FadingRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    <FadeIn>
      <Component {...props}/>
    </FadeIn>
  )}/>
)

<FadingRoute path="/cool" component={Something}/>

2.3.3. children: func

有時無論路徑是否匹配都要渲染組件借宵,這種情況下使用 children 渲染方式幌衣,它和 render 方式類似只是多了一個匹配過程。

<ul>
  <ListItemLink to="/somewhere"/>
  <ListItemLink to="/somewhere-else"/>
</ul>

const ListItemLink = ({ to, ...rest }) => (
  <Route path={to} children={({ match }) => (
    <li className={match ? 'active' : ''}>
      <Link to={to} {...rest}/>
    </li>
  )}/>
)

注意: component 和 render 方式都優(yōu)先于 children 方式暇务,因此無法在一個 <Route> 組件中同時使用泼掠。

2.3.4. path: string

一個符合正則的有效 URL 路徑怔软,當 Route 中沒有 path 時,該路由總是匹配 择镇。

<Route path="/users/:id" component={User}/>

2.3.5. exact: bool

完全匹配的選項挡逼。

<Route exact path="/one" component={About}/>

完全匹配的邏輯:

path location.pathname exact matches?
/one /one/two true no
/one /one/two false yes

2.4. history

<Route> 組件相關的還有三個對象,分別是 history腻豌,match家坎, location。在這節(jié)中我們先來了解 history 這個可變對象吝梅。

history 是 React-Router 極少的依賴模塊之一虱疏,它主要作用是在不同的環(huán)境下管理會話。經常會使用到的 history 有:

  • browser history:DOM 的具體應用苏携,支持 HTML5 的 history API做瞪。
  • hash history:DOM 的具體應用,用以兼容老版本的瀏覽器右冻。
  • memory history:在無 DOM 或測試環(huán)境下通過內存方式處理來實施装蓬,適合 React-Native。

和history 對象相關的主要屬性和方法有:

  • length:history 棧中的狀態(tài)路徑個數纱扭。
  • action:當前的操作牍帚,字符串形式(PUSH, REPLACE, POP)
  • location:當前的狀態(tài)路徑。
    • pathname: URL 路徑字段
    • search:URL 查詢字段
    • hash:URL 的 hash 字段
    • state:路徑的狀態(tài)乳蛾,只在 browser 和 memory history 中有效暗赶。
  • push(path, [state]):將狀態(tài)路徑壓入 history 棧。
  • replace(path, [state]):替換當前的狀態(tài)路徑肃叶。
  • go(n):向前移動當前指針 n 次蹂随。
  • goBack()go(-1)
  • goForward()go(1)
  • block(prompt):暫時阻止導航(“您確定離開”)。

history 是可變的因惭,因此狀態(tài)路徑的訪問方式推薦使用 <Route> 的屬性(props)里間接獲取糙及,而不是直接使用 history.location。這可以保證在 React 生存周期里能夠獲得正確的比較筛欢。

class Comp extends React.Component {
  componentWillReceiveProps(nextProps) {
    // 返回 true
    const locationChanged = nextProps.location !== this.props.location

    // 錯誤,總是 false唇聘,因為 history 是可變的版姑。
    const locationChanged = nextProps.history.location !== this.props.history.location
  }
}

<Route component={Comp}/>

2.5. location

在本文中我一直稱之為狀態(tài)路徑,意思很明顯就是具有狀態(tài)的路徑迟郎。該對象的與 url 對應剥险,其具體屬性如下:

{
  key: 'ac3df4', // not with HashHistory!
  pathname: '/somewhere'
  search: '?some=search-string',
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

狀態(tài)路徑會在以下幾個位置由 React-Router 給出:

  • Route component 由 this.props.location 給出
  • Route render({location}) => () 給出
  • Route children({location}) => () 給出
  • withRouter 由 this.props.location 給出

你也可以在這幾個位置定義狀態(tài)路徑:

  • Link to
  • Redirect to
  • history.push
  • history.replace

狀態(tài)路徑中的狀態(tài)可用于 UI 分支的顯示等情景。你也可以通過 <Route><Switch> 組件傳遞狀態(tài)路徑宪肖,這在動畫顯示和延遲導航中非常有用表制。

2.6. match

match 對象用以描述 <Route path> 匹配 URL 的結果健爬,其實就是 <Route> 組件的 state ,<Route> 組件根據 match 對象進行狀態(tài)轉換么介,主要的屬性有:

  • params:鍵值對對象對應正則模式中的參數字段娜遵。
  • isExact:是否完全匹配。
  • path:匹配使用的正則模式壤短。
  • url:所匹配的 URL 路徑字段设拟。

可以在下面位置訪問該對象,意味著將 <Route> 的 state 傳遞為子組件的 props:

  • Route component 由 this.props.match 給出
  • Route render 由 ({match}) => () 給出
  • Route children 由 ({match}) => () 給出
  • withRouter 由 this.props.match 給出

如果路由中沒有 path久脯,將匹配所有路徑纳胧, match 對象就是其父對象的 match。

3. API 查漏補缺

官方教程中給出了許多實例帘撰,如果了解了上述 API 大部分代碼都能看懂跑慕,還有一些 API 主要是增強使用的,為程序員在編程中提供了更多選擇和便利摧找。

3.1. <Redirect>

執(zhí)行 <Redirect> 將會使應用導航到一個新的位置核行,同時在 history 棧中新位置替換當前位置。因為是替換操作所以在 <Redirect> 中通常會使用狀態(tài)路徑慰于,并在狀態(tài)路徑中記錄當前位置钮科,以實現回溯路徑的操作。

import { Route, Redirect } from 'react-router'

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

3.1.1. to: string || object

重定向的位置婆赠。

3.1.2. push: bool

該屬性為 true 時绵脯,history 棧的操作不再是替換操作而是壓入新的位置元素。

<Redirect push to="/somewhere/else"/>

3.1.3. from: string

表明從那個位置進行重定向操作的休里。該屬性只有在 <Switch> 組件中才有效蛆挫。實際上該屬性就是 <Route> 組件中 path 屬性的別稱,與此同理還可以使用 exact妙黍, strict悴侵, path

<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>

3.2. <Switch>

組件和語法中的 switch 功能類似拭嫁,執(zhí)行第一個匹配的路由可免。這個邏輯很直觀也就是排他性,主要解決使用多個 <Route> 時多個路由同時匹配的問題做粤。

<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>

如果 URL 是 /about 浇借,那么上述三個路由都匹配,此時希望只有第一個路由渲染怕品,就需要使用 <Switch> 組件了:

import { Switch, Route } from 'react-router'

<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>

3.2.1. location: object

如果 <Switch> 組件中給出了 location 屬性妇垢,子元素將不再與當前位置或 URL 進行匹配操作,而是與該屬性進行匹配,并且匹配的子元素的 location 值將被覆蓋為 <Switch> 的 location 屬性值闯估。

3.2.2. children: node

<Switch> 組件中子元素可以是 <Route><Redirect> 組件灼舍, <Route> 使用 path 屬性進行匹配,而 <Redirect> 使用 from 屬性進行匹配涨薪。

<Switch>
  <Route exact path="/" component={Home}/>

  <Route path="/users" component={Users}/>
  <Redirect from="/accounts" to="/users"/>

  <Route component={NoMatch}/>
</Switch>

3.3. <NavLink>

特殊的 <Link> 組件骑素,主要作用是實現導航欄。

activeClassName: string

當鏈接激活時的類名尤辱,默認是 active 砂豌。

activeStyle: object

鏈接激活時的樣式。

<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>FAQs</NavLink>

isActive: func

在鏈接匹配的基礎上添加邏輯以決定是否該鏈接激活光督。

// only consider an event active if its event id is an odd number
const oddEvent = (match, location) => {
  if (!match) {
    return false
  }
  const eventID = parseInt(match.params.eventID)
  return !isNaN(eventID) && eventID % 2 === 1
}

<NavLink
  to="/events/123"
  isActive={oddEvent}
>Event 123</NavLink>

其他屬性 exact, strict, location 參考 <Link> 阳距。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市结借,隨后出現的幾起案子筐摘,更是在濱河造成了極大的恐慌,老刑警劉巖船老,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咖熟,死亡現場離奇詭異,居然都是意外死亡柳畔,警方通過查閱死者的電腦和手機馍管,發(fā)現死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薪韩,“玉大人确沸,你說我怎么就攤上這事》荩” “怎么了罗捎?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拉盾。 經常有香客問我桨菜,道長,這世上最難降的妖魔是什么捉偏? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任倒得,我火速辦了婚禮,結果婚禮上夭禽,老公的妹妹穿的比我還像新娘屎暇。我一直安慰自己,他們只是感情好驻粟,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般蜀撑。 火紅的嫁衣襯著肌膚如雪挤巡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天酷麦,我揣著相機與錄音矿卑,去河邊找鬼。 笑死沃饶,一個胖子當著我的面吹牛母廷,可吹牛的內容都是我干的。 我是一名探鬼主播糊肤,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼琴昆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了馆揉?” 一聲冷哼從身側響起业舍,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎升酣,沒想到半個月后舷暮,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡噩茄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年下面,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绩聘。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡沥割,死狀恐怖,靈堂內的尸體忽然破棺而出君纫,到底是詐尸還是另有隱情驯遇,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布蓄髓,位于F島的核電站叉庐,受9級特大地震影響,放射性物質發(fā)生泄漏会喝。R本人自食惡果不足惜陡叠,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肢执。 院中可真熱鬧枉阵,春花似錦、人聲如沸预茄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拙徽,卻和暖如春刨沦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膘怕。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工想诅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岛心。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓来破,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忘古。 傳聞我的和親對象是個殘疾皇子徘禁,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容