前端技術 | react-router,去中心化式路由

飛久發表於2019-05-08

1.靜態路由 vs. 動態路由

在Web前端開發中,我們經常會需要處理頁面路由問題。習慣上,路由資訊會在一個地方集中配置好,我們可以稱之為“靜態路由”,或者叫“中心化式路由”。以react-router v3版本為例,程式碼類似下面這樣:

import { Router, Route, IndexRoute, browserHistory } from 'react-router'

const App = () => (
  <Router history={browserHistory}>
    <Route path="/" component={RootPage}>
      <IndexRoute component={HomePage} />
      <Route path="/users" component={UsersPage} />
    </Route>
  </Router>
)

render(<App />, document.getElementById('app'))
複製程式碼

可以看到,在程式的頂層元件上配置好了所有路由資訊,並通過巢狀關係體現不同的層次。但是,react-router v4版本進行了革命性的改動,使之更加符合React的“元件化”思想,我們可以稱之為“動態路由”,或者借用區塊鏈中的術語,稱之為“去中心化路由”。用v4版本改寫後的程式碼類似於下面這樣:

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

const App = () => (
  <BrowserRouter>
    <RootPage />
  </BrowserRouter>
)

const RootPage = () => (
  <div>
    <Route path="/" exact component={HomePage} />
    <Route path="/users" component={UsersPage} />
  </div>
)

render(<App />, document.getElementById('app'))
複製程式碼

可以發現,路由的配置不再是全部位於頂層元件中了,而是分散在不同的元件中,通過元件的巢狀關係來實現路由的層次。另外,和靜態路由事先定義好所有頁面不同,動態路由可以在渲染時根據路徑匹配結果,動態決定渲染哪些元件,這樣就可以充分實現頁面的複用,減少重複渲染。下面依次介紹react-router v4的相關元件。

2.Router

react-router內部實際上是利用了瀏覽器的history API,因此在v3版本中我們需要在頂層的<Router>元件中傳入一個history屬性。在v4版本中為我們封裝了一個<BrowserRouter>元件,我們需要把它包在所有元件的最外層:

import { BrowserRouter } from 'react-router-dom'
const App = () => (
  <BrowserRouter>
    ... ...
  </BrowserRouter>
)
複製程式碼

另外還有一個<HashRouter>元件,是為了相容以前版本的瀏覽器,已經不推薦使用了。

3.路由匹配

路由匹配主要涉及兩個元件:<Route>和<Switch>,下面分別介紹。

3.1 <Route>

<Route>元件包含3類屬性:匹配屬性、渲染方法、路由屬性

3.1.1 匹配屬性

這些屬性是為了設定url匹配規則。

(1) path:路由匹配正規表示式

舉例:

<Route path='/' />:匹配以/開頭的路徑,比如/,/login,/login/alice

<Route path='/login/:username' />:可以匹配/login/alice,引數可以通過props.match.params.username獲得

(2) exact:精確匹配

舉例:

<Route path='/' exact />:只能精確匹配/,不能匹配/login

在v4版本里取消了<IndexRoute>元件,因此如果你希望在根路徑顯示某個頁面的話,可以用下面的方式:

<div>
  <Route path="/" exact component={HomePage} />
  ... ...
</div>
複製程式碼

(3) strict:嚴格匹配

如果path的最後帶有/,那麼url必須也帶有/才能匹配。舉例:

<Route path='/login/' strict />:可以匹配/login/,不能匹配/login

(4) sensitive:大小寫敏感

3.1.2 渲染方法

這些屬性指明在匹配上路由後如何渲染。

(1) component:渲染元件

這是最常見的方式,路由匹配時渲染一個指定元件

<Route path='/users' component={UsersPage} />

(2) render:內聯渲染

component方式會通過React.createElement建立新元素,然後mount到DOM上,如果需要頻繁更新的話這種方式效率會比較低,這時候你可以選擇內聯渲染方式。

<Route path='/users' render={props => (
  <div>
    <Component {...props}/>
  </div>
)}/>
複製程式碼

(3) children:根據path匹配結果渲染子元素

前面兩種渲染方法都是隻有path匹配時才渲染,children渲染方式則比較特殊,它是針對子元素的,並且只返回一個match結果,你可以根據match結果自行決定如何渲染子元素。舉個例子,在一個列表中,我們希望高亮那些被匹配上的元素:

<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>
    )}
  />
);
複製程式碼
3.1.3 路由屬性

這些屬性可以在待渲染元件中通過props訪問到。

(1) match

match屬性中包含了路由匹配相關的引數:

  • params:引數鍵值對(比如前面的username)
  • isExact:是否精確匹配(是否指定了exact)
  • path:正規表示式(如/login/:username)
  • url:和path匹配上的部分(入/login/alice)

(2) location

location中包含了當前url相關的引數:

  • pathname:當前url
  • search:url中的查詢字串(如?username=a&sort=b)
  • state:攜帶的特定狀態引數,是一個物件(如{ XXX: 'xxx' })

(3) history

history就是瀏覽器提供的history API,提供了一些路由函式:

  • push(path, [state]):頁面跳轉
  • replace(path, [state]):重定向
  • go(n):直接跳轉到第n個頁面
  • goBack():等價於go(-1)
  • goForward():等價於go(1)

3.2 <Switch>

如果我們希望設定一系列的路由匹配規則,只渲染第一個匹配上的元件,該如何實現呢?這時候你就需要用到<Switch>元件。<Switch>中包含一組<Route>或者<Redirect>,只渲染第一個匹配的路由

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>
複製程式碼

4.導航

react-router提供了一系列的導航元件,基本上就是<a>標籤的一個封裝。主要包括3種:

  • <Link>:最終渲染為<a>
  • <NavLink>:path匹配時顯示為高亮樣式
  • <Redirect>:重定向

舉例:

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

<Link to="/about">About</Link>
<NavLink to="/faq" activeClassName="selected">
  FAQs
</NavLink>
複製程式碼

另外,react-router還提供了其他一些工具庫,比如支援元件的動態import(下載)的react-loadable,支援CSS轉場動畫的react-transition-group,有興趣的朋友可以自行研究一下。

最後以一張思維導圖結束本篇文章:

前端技術 | react-router,去中心化式路由

相關文章