[ 一塊兒學React系列 -- 11 ] React-Router4 (1)

2019年不知不覺已通過去19天了,有沒有給本身作個總結?有沒有給明年作個計劃?
固然筆者已經作好了明年的工做、學習計劃;同時也包括該系列博客剩下的博文計劃,目前還剩4篇:分別是兩篇React-Router4和兩篇immutability-helper。本篇是React-Router4的第一篇,在正式開始以前你們能夠先看下實際效果,這是筆者用React-Router4寫的例子。react

React-Router4

實際上,筆者接觸的第一個React中的路由模塊就是React-Router,只不過當時仍是v3版本。由於沒有太多深刻得學習研究,因此在這裏不會對v4以前的版本做介紹或者評價(其實並不表明筆者對v4版本有深刻的研究學習,汗...)。下面列舉些React-Router4中須要知道的一些概念或者emmmm...小知識點。git

React-Router4中的包

React-Router4中的包主要有三個react-routerreact-router-domreact-router-native。據考證(手動斜眼笑),React-Router4已經將前身切分了出來分別整合成了單獨的module。其實筆者一開始看到也挺懵逼的,這三個包究竟是什麼玩意?github

react-router:「The core of React Router」。簡單說就是邏輯代碼。
react-router-dom:「DOM bindings for React Router」。這個模塊不只僅包含了react-router的模塊,還包含了頁面渲染功能,也就是能夠在頁面上顯示。
react-router-native:「React Native bindings for React Router」。這個也很好理解,就是能夠在React-Native中使用。

路由根節點

React-Router4的理念是一切皆組件React-router-dom則提供了兩個路由根節點:HashRouter和BrowserRouter。express

HashRouter: 經過hash值來對路由進行控制,並且你會發現一個現象就是url中會有個 #,例如localhost:3000/#。對於筆者這種有強迫症的人來講怎麼能忍?因此筆者就沒再碰這個組件。
BrowserRouter: BrowserRouter就相對舒服點。它的原理是使用HTML5 history API (pushState, replaceState, popState)來使你的內容隨着url動態改變的,而不會出現莫名其妙的 #

React-Router4的理念

上面提到:React-Router4的理念是一切皆組件(如下統一稱「組件」)。咱們v4以前的版本都須要在一個js文件中配置整個項目的路由信息而後在App.js(以create-react-app腳手架建立的React項目爲例)引入並使用。而在React-Router4中則將全部的功能以React組件的形式提供出來,意思就是說咱們能夠像使用普通組件同樣使用路由組件並最終在項目中造成一棵路由樹。在這裏筆者要注重說下,一個項目中儘可能只有一棵路由樹,除非有特殊需求,不然的話會形成一些奇怪的問題。通俗點說就是不要在一棵樹上栽另外一棵樹。下面用一張圖來簡單展現下React-Router4的使用:react-router

clipboard.png

如圖是React-Router4使用規則和最簡單的使用實踐。
固然這種寫法(我稱它叫組件型路由,下同)和以前的配置型路由哪一個更好用是青菜蘿蔔的問題,不須要太多糾結,本身喜歡就好。接下來的介紹都是筆者經過學習官方文檔並作了一些實踐後的心得。若有不當或者錯誤,歡迎指正。對於第一次學習該模塊的童鞋,筆者建議將代碼下載下來一遍看代碼一遍看本文,也能夠打開筆者的demo,這樣會更深入。app

經常使用路由組件介紹

下面這段代碼是該demo根路由配置的代碼(其實不存在什麼根路由概念,只是筆者喜歡這麼叫它)。從這裏就能明顯看出來react-router3和4兩個版本的差別。react-router3有本身獨立的一個配置中心文件,而react-router4則把跳轉的規則嵌入到實際代碼中,不須要額外維護一個路由配置文件。從這點來講的確方便了很多,也迎合React一切皆組件的理念。dom

<Router>
    <div className={AppStyle["Container-body"]}>
        <nav className={AppStyle["App-nav-list"]}>
            <ul>
                <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/basic">基礎路由</NavLink></li>
                <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/param">帶參路由</NavLink></li>
                <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/nesting">嵌套路由</NavLink></li>
                <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/ambiguous">曖昧匹配</NavLink></li>
                <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/auth">權限路由</NavLink></li>
                <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/404">404</NavLink></li>
            </ul>
        </nav>
        <div className={AppStyle.content}>
            <Switch>
                <Redirect exact from="/" to="/basic" />
                <Route path="/basic" component={renderBasicRouter} />

                <Route path="/param" component={RouterWithParameters} />
                <Route path="/ambiguous" component={renderAmbiguousRouter} />
                <Route path="/nesting" component={renderNestingRouter} />
                <Route path="/auth" component={authenticationRouter} />
                <Route component={NoMatch} />
            </Switch>
        </div>
    </div>
</Router>

從這段代碼中須要瞭解的概念包括:RouterNavLinkRouteRedirectexactSwitch學習

Router

上面已經介紹過了,這裏筆者用的是BrowserRouter測試

NavLink

NavLink能夠觸發路由的跳轉,固然相似的組件還有Link。它們均可以經過指定屬性to的值來告訴Router咱們要跳轉到那個Route,實際上NavLink(Link)Route本就已經經過topath兩個屬性創建起關係了。而NavLink與Link的區別在於各自API的數量,由於NavLink可用的API相對較多,因此筆者更青睞NavLink。具體API有興趣的能夠自行Google,經常使用的API筆者已經在項目中使用。網站

Route

Route組件是React Router4中主要的組成單位,能夠認爲是NavLink或Link的路由入口。在這裏筆者要重點Tip一下,上一條說NavLink或Link能夠觸發路由的跳轉,實際上它們的實現流程是這樣的:

NavLink(Link)改變地址欄的 pathname,Router會根據pathname去匹配它子組件中的Route中的 path屬性,一旦匹配上就會渲染該Route對應的組件`。因此NavLink(Link)與Route並無並存的關係,由於NavLink(Link)只是用來改變pathname,不會直接去調用任何API。因此當咱們在地址欄直接手動輸入路由,也會發生路由渲染。

具體匹配規則請參考path-to-regexp,或者經過這個網站進行測試。

Redirect

Redirect至關於轉發器。Router內部去匹配路由入口的時候也會去匹配Redirect的from屬性值。一旦匹配到了Redirect,Redirect就會將這個跳轉請求轉向本身的to屬性值對應的路由。因此這個過程能夠這樣理解:當咱們訪問頁面路徑的時候,好比:http://ip:3001/,就會捕獲到'/'這個路由跳轉請求,Router開始在Route中匹配隨後匹配到了Redirect,Redirect自行發起路由跳轉請求'/basic',Router開始像往常同樣在Route中匹配直到匹配到了path爲"/basic"的Route,隨後對Route對應的component進行渲染。

exact

exact將該Route標示爲嚴格匹配路由。什麼叫嚴格匹配路由?就是pathname必須與Route的path屬性值徹底一致纔算匹配上。例如:

pathname path 匹配結果
/home /home 能夠匹配
/home/child /home 沒法匹配
這裏Tip下:
若是某個Route對應的組件中也有Route,那麼 千萬不要 在這個Route中加 exact,否則你會發現徹底匹配不到子路由。切記,由於筆者最近剛踩過這個坑。因此這裏筆者建議你們只在 葉子Route中使用exact,也就是最後一級Route中使用exact,固然 exact '/' 除外

Switch

Switch能夠將多個Route包裹成一組Route,當進行路由匹配時候,該組中的路由一旦發生匹配那麼就不會匹配改組中剩下的路由。也就是說該組中的路由最多隻會被匹配到一個

Route的component屬性

追加一條。
Route的component屬性對應的屬性值一般是一個組件或者一個方法。而若是是方法的話,好比例子中:

const renderBasicRouter = ({ match }) => <Basic url={match.url} path={match.path} />;

那麼傳入的參數爲:
clipboard.png

因此若是在跳轉前有什麼特殊邏輯須要處理,好比咱們想讓不一樣的頁面有不一樣的前綴,好比例子中的/basic/Home/param/home等,那麼就能夠如例子同樣處理。
但若是不須要特殊處理的話,直接把組件放到component屬性下便可。固然router相關的參數也會經過props傳給該組件。

這裏Tip下:
一、Route中還有一個render屬性,也能夠用來渲染組件,可是當咱們渲染被Redux處理過的組件時候可能會有問題,由於Redux會在原組件基礎上多包裹一層。
二、若是

固然經常使用路由組件還不只僅這些,後續例子會有補充。

基礎路由

基礎路由的使用比較簡單,前面的介紹其實已經把它基礎使用方法已經說了。不過筆者這裏有個小tip:

match.url 經常使用於NavLink或者Link中拼接路由路徑,好比:
<NavLink to={`${URL}/Home`}></NavLink>
match.path經常使用於Route中拼接路由入口路徑,好比:
<Route path={`${PATH}/Home`} component={HomePage} />
這裏Tip下:
當URL中不帶有任何參數的時候,match.path和match.url徹底一致。若是帶有參數的話可能會有點編碼上的差別。

帶參路由

帶參路由在實際開發中用的比較多,這裏只介紹location.pathname中的參數,location.search筆者沒有研究過因此就不說了,省得誤導你們。咱們能夠先看下demo例子的部分代碼:

<nav className={Style["Params-nav-list"]}>
    <ul>
        <li><NavLink exact activeStyle={{ fontWeight: 'bold', color: 'red' }} to={`${URL}/name`}>/name</NavLink></li>
        <li><NavLink exact activeStyle={{ fontWeight: 'bold', color: 'red' }} to={`${URL}/name/Mario`}>/name/Mario</NavLink></li>
        <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to={`${URL}/check/true`}>/check/true</NavLink></li>
    </ul>
</nav>
<div className={Style.content}>
    <Switch>
        <Route exact path={`${PATH}/name/:name`} component={Name} />
        <Route exact path={`${PATH}/name`} component={Name1} />
        <Route exact path={`${PATH}/check/:check(true|false)`} component={Check} />
        <Route component={NoMatch} />
    </Switch>
</div>

這裏能夠看到Route的配置有點不一樣,用過express的朋友都知道,路由中經過 :xxx 來標示這是一個參數。其實這裏也同樣。如例子所示咱們在 path={${PATH}/name/:name}經過 :name 來標示這是一個參數。而後對應的導航也有 to={${URL}/name/Mario}。因此這個流程就至關於:導航到/name路由而且傳 name爲Mario的參數。這個參數能夠在對應組件的props中拿到(this.props.match.prams.name)。咱們還看到 path={${PATH}/check/:check(true|false)},在參數後有個括號而且裏面還有管道符,其實這是限定check的參數值必須爲true或者false這兩個。細心的朋友可能注意到,我在每一個Route中加了exact,這樣作的好處是能夠不用考慮Route的放置次序。舉個例子解釋下:若是咱們想查看某我的的信息,那麼跳轉路由應該是 /user/4,但若是Route中有:

<Switch>
    <Route exact path={/user} component={User} />
    <Route exact path={/user/:id} component={User} />
</Switch>

那麼/user/4就會在匹配到/user後停下渲染User組件而且忽略了參數。有人說把Switch去掉不就好了嗎?並非,那麼/user/4會同時匹配上這兩個路由而且什麼都不會渲染,由於它懵逼了,不知道渲染哪一個。因此這種狀況下須要調整它們的位置

<Switch>
    <Route exact path={/user/:id} component={User} />
    <Route exact path={/user} component={User} />
</Switch>

這樣就不會出現上述問題了。可是若是在每個Route中使用exact(前提是這個Route是葉子Route),就不用考慮Route的次序問題了。

404路由

有時候會因爲各類問題出現匹配不到任何Route的狀況,這個時候爲了更好的用戶體驗,咱們會配置一個404路由,形如:

<div className={Style.content}>
    <Switch>
        <Route exact path={`${PATH}/name/:name`} component={Name} />
        <Route exact path={`${PATH}/name`} component={Name1} />
        <Route exact path={`${PATH}/check/:check(true|false)`} component={Check} />
        <Route component={NoMatch} />
    </Switch>
</div>

不過筆者發如今根路由中配置一個404後沒法全局抓取路由404,不知道是本就如此仍是用法有誤,因此筆者在每一級路由中都配置了 <Route component={NoMatch} />。寫法很簡單,照着寫就行了,component中傳入顯示404信息的組件便可。

順便加一下demo源碼

相關文章
相關標籤/搜索