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,有興趣的朋友可以自行研究一下。
最後以一張思維導圖結束本篇文章: