React-router總結


版本

v3和v4有一些差距:

https://blog.csdn.net/qq_35484341/article/details/80500237

以下的總結,都是基於V4的

官方文檔:https://reacttraining.com/react-router/web/guides/quick-start

核心組件和用法

<BrowserRouter/>   
<HashRouter/>
<Route/>
<Switch/>
<Redirect/>
<Link/> (<NavLink/>)
withRouter

不多解釋,先上實例:

<BrowserRouter>
   <Link to="/">主頁</Link>
   <Link to="/recommend">推薦</Link>
   <Link to="/detail/1">詳情</Link>

   <Route path="/" exact component={Home}/>
   <Route path="/detail/:id" exact component={Detail}/>
   <Route path="/recommend" exact component={Recomm}/>
</BrowserRouter>

BrowserRouter和HashRouter

這兩者可以理解為路由的作用域,所有的Link組件和Route組件都必須在其內部。

瀏覽器路由和哈希路由的區別:

  • BrowserRouter中的URL指向真實的url,當頁面刷新(或直接操作url並回車)時,將產生指向該路徑的url請求,如果服務器端沒有配置該路徑,則返回404。在項目上線后,需要我們去配置服務端。(通過Link並不會向后端發送真實請求)
  • HashRouter中#后面的uri並不是真實的請求路徑,對於后端來說,全都指向同一個地址。另外哈希歷史記錄不支持loaction.key和loaction.state,當通過state傳遞參數的時候,無法從歷史記錄中獲取到,可能會導致頁面顯示異常。

在BrowserRouter模式下,如何在服務端配置呢?

  • Nginx配置(前端項目打包),將任何URL訪問都指向index.html
  • 后端服務配置(前端項目打包),將任何URL訪問都指向index.html
  • 開啟前端服務(需要進行Nginx反向代理)

Link

被渲染成a標簽,跟href屬性的所用類似。點擊Link標簽和a標簽,看到的效果是一樣的,但我們推薦<Link>,使用它才是單頁面應用,瀏覽器不會請求頁面;而使用a標簽,會重新請求頁面。

<Link to='/path' />
<Link to={
   pathname, // 路徑,即 to 的字符串用法
   search,  // search
   hash,  // hash
   state,  // state 對象
   action,  // location 類型,在點擊 Link 時為 PUSH,瀏覽器前進后退時為 POP,調用 
   replaceState 方法時為 REPLACE
   key,  // 用於操作 sessionStorage 存取 state 對象
} />
<Link to='/path' replace={true} /> // 覆蓋當前路徑

NavLink

Link的加強版

  • activeClassName(string):設置選中樣式,默認樣式名為active
  • activeStyle(object):當元素被選中時,為此元素添加樣式
  • exact(bool):為true時,只有當導致和完全匹配class和style才會應用
  • strict(bool):為true時,在確定為位置是否與當前URL匹配時,將考慮位置pathname后的斜線
  • isActive(func):判斷鏈接是否激活的額外邏輯的功能。可以做導航守衛

Route

具體的路由規則

屬性:

// 渲染目標
component // 渲染組件
render  
children

// 地址匹配
path
<Route path="/hello/:name"> 
name為參數名,可以在其渲染組件中獲取 this.props.params.name
<Route path="/files/*.*">
匹配 /files/hello.jpg  、   /files/hello.html
<Route path="/files/*">
匹配 /files/  、 /files/a   、 /files/a/b

exact  // 完全匹配的意思,排他

Switch

有<Switch>嵌套,則其中的<Route>在路徑相同的情況下,只匹配第一個,這個可以避免重復匹配。

無<Switch>嵌套,則其中的<Route>在路徑相同的情況下全都會匹配,包括上級路徑。

Rediret

重定向到新地址

<Redirect to='/path' />

Prompt

當用戶准備離開該頁面時,彈出提示

message: 字符串/函數,離開頁面時的提示信息
when:布爾值,通過設定條件決定是否啟用該組件

例如:

<Prompt message="您確定要離開該頁面嗎?" when={this.state.isOpen} />
<Prompt
   message = {() => {this.state.isOpen? false: "您確定要離開該頁面嗎?"}}
/>

對象和方法 

被Route綁定的渲染組件,總是被傳入三個屬性(對象):history、location、match。在渲染組件中也會有很多其他組件,這些組件內部如果想要獲取三個對象,需要withRouter(通過裝飾器或函數調用的形式都可)。

我們可以用這三個對象完成很多事情。

history

history實現對會話歷史的管理。

length: number   瀏覽歷史堆棧中的條目數
action: string   路由跳轉到當前頁面執行的動作,分為 PUSH, REPLACE, POP
location: object   當前訪問地址信息組成的對象,具有如下屬性:
pathname: string   URL路徑
search: string   URL中的查詢字符串
hash: string   URL的 hash 片段
state: string   例如執行 push(path, state) 操作時,location 的 state 將被提供到堆棧信息里,state 只有在 browser 和 memory history 有效。
push(path, [state])   在歷史堆棧信息里加入一個新條目。
replace(path, [state])   在歷史堆棧信息里替換掉當前的條目
go(n)   將 history 堆棧中的指針向前移動 n。
goBack()   等同於 go(-1)
goForward   等同於 go(1)
block(prompt)   阻止跳轉

location

loaction指當前的位置

{
  hash: '#sdfas',
  key: 'sdfad1'
  pathname: '/about',
  search: '?name=minooo'
  state: {
    price: 123
  }
}

可以在不同場景中使用:

<Link to={location} />
<NaviveLink to={location} />
<Redirect to={location />
history.push(location)
history.replace(location)

match

match對象包含了<Route>如何與URL匹配的信息。

params: object 路徑參數,通過解析 URL 中的動態部分獲得鍵值對
isExact: bool 為 true 時,整個 URL 都需要匹配
path: string 用來匹配的路徑模式,用於創建嵌套的 <Route>
url: string URL 匹配的部分,用於嵌套的 <Link>

路由之間的傳值

大致有三種方法:

params

//路由設置
<Route path=' /user/:id '   component={User} />
// 傳值
<Link to='/user/2' />
this.props.history.push("/user/2");
// 獲取值
this.props.match.params.id

URL參數

//路由設置
<Route path=' /user '   component={User} />
// 傳值
<Link to='/user?id=2' />
this.props.history.push("/user?id=2");
// 獲取值
this.props.location.seacrh // 獲得"?id=2",將其進行拆解即可獲得

location對象(哈希路由無法利用)

又有兩種方式:

(1)loaction.state。傳的參數是加密的

// 路由設置
<Route path=' /user '   component={User}></Route>
// 傳值
<Link to={{ pathname:' /user',state:{id:123},search:'?sort=name',hash:'#the-hash'}}> 
this.props.history.push({pathname:' /user',state:{id:123},search:'?sort=name',hash:'#the-hash'});
// 獲取值
this.props.location.state.id

(2)自定義屬性

// 路由設置
<Route path=' /user '   component={User}></Route>
// 傳值
<Link to={{ pathname:' /user',abc:{id:123},search:'?sort=name',hash:'#the-hash'}}> 
this.props.history.push({pathname:' /user',abc:{id:123},search:'?sort=name',hash:'#the-hash'});
// 獲取值
this.props.location.abc.id

導航守衛的實現

在react-router中,並沒有提供導航守衛相關的API,作者這樣描述:你可以在渲染功能中實現此功能,JSX不需要API,因為它更靈活。

首先,導航守衛在業務層面可能有三種表現:

  1. 根據狀態(如登錄/未登錄)和身份(等級)將路由進行限制。低等級的用戶根本沒有定義某些路由。
  2. 路由在任何時候都是完整的,只是根據狀態和身份,將入口進行限制(某些Link組件不顯示)。沒有任何操作能夠導向沒有權限訪問的路由地址。
  3. 不對入口進行限制,根據狀態和身份,將某些特定的路由添加攔截。

在這幾種表現中,2和3並不相互沖突。現在有兩種思路實現導航守衛:

  • 寫一個路由配置表,寫一個高階組件,導航守衛的功能由高階組件完成,所有與路由綁定的組件都被高階組件修飾。(對應業務場景3)
  • 寫一個路由配置表,定義一個組件:根據路由配置生成最終的<Route>。對於用戶沒有權限的路由,可以控制不將其渲染。(對應業務場景1)
  • 寫一個路由配置表,寫一個高階組件,將是否渲染入口的邏輯寫在高階組件中,所有可能被隱藏的入口都被此高階組件修飾。(對應業務場景2)

示例1(業務場景1):

// 路由配置
const routerConfig = [
    {
        path:'/',
        component:HomePage,
        auth:false, 
    },{
        path:'/home',
        component:HomePage,
        auth:true, // 表示必須登錄才能訪問
    },{
        path:'/login',
        component:LoginPage,
    },{
        path:'/404',
        component:ErrorPage
    }
];

class RouterTab extends React.Component{
    render(){
        return(
            <HashRouter>
                <Switch>
                    <RouteGenerator config={routerConfig} />
                </Switch>
            </HashRouter>
        );
    }
}

class RouterGenerator extends React.Coponent{
   // 在這里定義限制路由的邏輯(業務場景1)
   render(){
      const routeConf = this.props.config;
      routeConf.map((conf)=>{
         if(conf.auth) // 判斷該路由是否滿足渲染條件
         {
            if(login) // 判斷當前有沒有登錄
            {
               retrun <Route path={conf.path} component={conf.component} exact={conf.exact?true:false} />
            }
         }else
         {
            retrun <Route path={conf.path} component={conf.component} exact={conf.exact?true:false} />
         }
      });
   }
}

示例2(業務場景3):

// 將此高階組件修飾所有需要被守衛的路由組件
const routerGuard = (RouteConfig) => {
   retrun (WrappedComponent) => {
      rentrun class NewComponent extends React.Component {

         componentWillMount(){
            const path = this.props.location.pathname;
            const config = RouteConfig.find(f => f.path === path);
            if(!config)
            {
               this.props.history.push("/404");
            }
            if(config.auth && !login)
            {
               this.props.history.push("/login");
            }
         }

         render(){
            retrun(
               <WrappedComponent {...this.props} />
            )
         }
      }
   }
}

路由的異步加載 

如果項目特別大,打包后的js文件會特別大。在首次加載時,一次性地將整個js文件發送給瀏覽器,一次性地加載所有代碼,會對瀏覽器產生比較大的壓力,影響用戶體驗。我們希望的是,只加載當前訪問頁面相關的js代碼,當路由跳轉的時候,再請求相應頁面的js代碼。這需要使用第三方庫,做的比較優秀的有 react-loadable 和 react-async-component。

react-loadable的原理是將代碼進行分割,分割成若干個chunk.js,需要用到的時候就請求過來。

react-async-component為按需異步加載,使用Promise和高階組件實現,提高性能。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM