1. 程式人生 > 其它 >十四、react路由

十四、react路由

一、路由的使用

React Router官網:https://reactrouter.com/

安裝

1 npm i -S react-router-dom

1、相關元件

  • Router元件:包裹整個應用(單個具體的元件/根元件),一個React應用只需要使用一次
    • Router型別: HashRouter和BrowserRouter
      • HashRouter: 使用URL的雜湊值實現 (localhost:3000/#/first)
      • BrowserRouter:使用H5的history API實現(localhost3000/first)
    • HashRouter和BrowserRouter的不同點:
      • HashRouter使用URL的雜湊值實現,BrowserRouter使用H5的history API實現
      • HashRouter路徑中有#,BrowserRouter沒有#
      • HashRouter重新整理後會導致路由state引數的丟失,BrowserRouter不會
  • Link/NavLink元件:用於指定導航連結(a標籤),就是做宣告式導航的
    • 最終Link會編譯成a標籤,而to屬性會被編譯成 a標籤的href屬性
    • NavLink可以實現路由連結的高亮顯示,通過屬性activeClassName指定樣式名
  • Route元件:指定路由展示元件相關資訊(元件渲染)
    • path屬性:路由規則,這裡需要跟Link元件裡面to屬性的值一致
    • component屬性:展示的元件

注意:LinkRoute

元件必須被Router元件給包裹,否則報錯。

2、封裝NavLink元件

1、NavLink標籤是一個高亮顯示的導航標籤

2、NavLink標籤體內容是一個特殊的標籤屬性,通過this.props.children屬性可以獲取標籤體內容(即Home或About)

NavLink封裝

 1 import React,{Component} from 'react'
 2 import {NavLink} from 'react-router-dom'
 3 
 4 export default class MyNavLink extends Component{
 5   render(){
6 return( 7 <NavLink activeClassName="link-name" className="link-item" {...this.props}/> 8 ) 9 } 10 }

使用

1 <MyNavLink to="/home">Home</MyNavLink>
2 <MyNavLink to="/about">About</MyNavLink>

3、Switch元件

Switch元件,讓其去包裹路由的Route元件(Switch元件保證只渲染其中一個子路由)

1 <Switch>
2   {/*Switch只渲染第一個匹配的元件*/}
3   <Route path="/home" component={Home}></Route>
4   <Route path="/home" component={Test}></Route>
5   <Route path="/about" component={About}></Route>
6 </Switch>

二、路由導航方式

1、宣告式導航

src/index.js入口檔案中定義一個路由模式(Router元件包裹根元件)

 1 import React from "react";
 2 import ReactDOM from "react-dom";
 3 
 4 // 設定路由模式
 5 import {HashRouter as Router} from 'react-router-dom'
 6 
 7 // 定義 provider
 8 import { Provider } from "react-redux";
 9 import store from "./Store/index";
10 
11 import App from "./App";
12 
13 ReactDOM.render(
14   <Provider store={store}>
15       // 使用Router包裹根元件
16     <Router>
17       <App></App>
18     </Router>
19   </Provider>,
20 document.getElementById("root")
21 );

在根元件src/App.jsx中引入路由相關元件(也可以在具體的某個元件中使用Router元件)

 1 import React, { Component } from "react";
 2 import { HashRouter as Router, Route, Link } from "react-router-dom";
 3 
 4 import Cmp10 from "./Components/Cmp10";
 5 import Cmp11 from "./Components/Cmp11";
 6 
 7 class App extends Component {
 8   render() {
 9     return (
10       <Router>
11         <div>
12           <h1>導航區域</h1>
13           <div>
14             <ul>
15               <li>
16                 <Link to="/home">首頁</Link>
17               </li>
18               <li>
19                 <Link to="/news">新聞</Link>
20               </li>
21             </ul>
22           </div>
23         </div>
24         <Route path="/home" component={Cmp10}></Route>
25         <Route path="/news" component={Cmp11}></Route>
26       </Router>
27     );
28   }
29 }
30 export default App;

需要注意:

  刨除樣式的影響,Route元件在HTML程式碼中的位置決定了渲染後其在頁面中顯示的位置。如果Route放在最後,則其顯示的時候也在最後;若其放在渲染內容的最前面,相應的顯示也會在最開始。

2、程式設計式導航

路由元件與一般元件有三個重要的不同屬性:history、location、match

  • react-router-dom中通過history物件中的push/replace/go等方法實現程式設計式導航功能,這一點與之前的vue路由還是很相似的。
    • push:先進後出,可以返回檢視歷史跳轉情況
    • replace:替換,不能返回檢視歷史頁面
1 <MyNavLink replace to="/home">Home</MyNavLink>
1 this.props.history.push({
2   pathname: "/home",
3   search: "from=404",    // 表示傳遞查詢字串
4   state: {                    // 隱式傳參,位址列不體現
5     username: "admin",
6   },
7 });
8 
9 this.props.history.go(-1)

不要在根元件中使用程式設計式導航。

三、路由匹配

1、模糊匹配與嚴格匹配

模糊匹配(預設)

1 {/*MyNavLink中的/about/a/b匹配到Route中的/about"*/}
2 <MyNavLink to="/home">Home</MyNavLink>
3 <MyNavLink to="/about/a/b">About</MyNavLink>
4 
5 <Switch>
6   <Route path="/home" component={Home}></Route>
7   <Route path="/home" component={Test}></Route>
8   <Route path="/about" component={About}></Route>
9 </Switch>

嚴格匹配:exact屬性

1 <MyNavLink to="/home">Home</MyNavLink>
2 <MyNavLink to="/about/a/b">About</MyNavLink>
3 
4 <Switch>
5   <Route exact path="/home" component={Home}></Route>
6   <Route exact path="/home" component={Test}></Route>
7   <Route exact path="/about" component={About}></Route>
8 </Switch>

2、重定向路由

React的重定向路由有以下兩種寫法:

方式一:推薦

1 import { Redirect } from "react-router-dom"
2 
3 <Redirect from="/from" to="/to"></Redirect>
4 <Route path="/to" component={xxxx}></Route>

方式二 :不推薦

1 import { Route, Redirect } from "react-router-dom"
2 
3 <Route path="/from">
4     <Cmp></Cmp>
5     <Redirect to="/to" />
6 </Route>

3、404路由

專案中少不了404頁面的配置,在React裡面配置404頁面需要注意:

1 import NotFound from "./Components/404";
2 
3 <Route>
4     <NotFound></NotFound>
5 </Route>
6 //
7  <Route component={NotFound}></Route>
  • 需要用到Switch元件,讓其去包裹路由的Route元件(Switch元件保證只渲染其中一個子路由)
  • 在404路由的位置,不需要給定具體的路由匹配規則,不給path表示匹配*,即所有的路由都會匹配,因此用404路由一定要加Switch匹配一個路由。
  • 並不會因為當前是404路由/重定向路由而改變狀態碼,因為當前寫的是前端的內容,狀態碼是後端提供的,只有等後期上線以後才能有狀態碼。
 1 <div>
 2   <Link to="/home">家</Link> &emsp;
 3   <Link to="/news">新聞</Link>&emsp;
 4   <Link to="/about">關於</Link>&emsp;
 5   <Redirect from="/" to="/home"></Redirect>
 6   <Switch>
 7     <Route path="/home" component={Cmp11}></Route>
 8     <Route path="/news" component={Cmp12}></Route>
 9     <Route path="/about" component={Cmp13}></Route>
10     <Route component={NotFound}></Route>
11   </Switch>
12 </div>

四、巢狀路由

在有一些功能中,往往請求地址的字首是相同的,不同的只是後面一部份,此時就可以使用多級路由(路由巢狀)來實現此路由的定義實現。

例如,路由規則如下

1 admin/user
2 admin/goods

它們路由字首的admin是相同的,不同的只是後面一部份。

實現方式

  • 先定義個元件,用於負責匹配同一字首的路由,將匹配到的路由指向到具體的模組
1 <Route path="/admin" component={Admin}></Route>
  • 建立模組路由元件負責指定各個路由的去向
 1 render() {
 2   // 獲取字首,供後續地址做路由拼接
 3   let prefix = this.props.match.path;
 4   return (
 5     <div>
 6       <h1>歡迎使用後臺管理程式</h1>
 7       <Route path={`${prefix}/user`} component={User}></Route>
 8       <Route path={`${prefix}/goods`} component={Goods}></Route>
 9     </div>
10   );
11 }

五、路由懶載入

優化效能,讓路由按需載入

  • 引入react上的Suspense,包裹註冊的路由元件
  • Suspense用於展示路由未加載出來展示的元件
 1 import React, {Component, lazy, Suspense} from 'react'
 2 import {NavLink, Route} from 'react-router-dom'
 3 
 4 // Loading用於懶載入元件未加載出來的展示
 5 import Loading from './Loading'
 6 
 7 // import Home from './Home'
 8 // import News from './News'
 9 
10 const Home = lazy(() => {import('./Home')})
11 const News = lazy(() => {import('./News')})
12 
13 export default class Demo extends Component{
14   <div>
15     <h1>導航區域</h1>
16     <div>
17       <ul>
18         <li>
19           <Link to="/home">首頁</Link>
20         </li>
21         <li>
22           <Link to="/news">新聞</Link>
23         </li>
24       </ul>
25     </div>
26     <Suspense fallback={<Loading/>}>
27       <Route path="/home" component={Home}></Route>
28       <Route path="/news" component={News}></Route>
29     </Suspense>
30   </div>
31 }

六、路由引數

路由引數:在Route定義渲染元件時給定動態繫結的引數。

React路由傳參方式有三種:

1、動態路由引數(param)

/film/detail/:id形式傳遞的資料

1 <Link to={`/film/detail/${detailObj.id}`}></Link>

在目標頁面路由中傳遞

1 <Router path="/film/detail/:id" component={Detail}></Router>

在落地元件中通過this.props.match.params得到

2、查詢字串(search,也可叫query)

  • 通過位址列中的 ?key=value&key=value傳遞
  • 在落地元件中通過this.props.location.search得到

3、隱式傳參(state),通過位址列是觀察不到的

  • 不適合寫在宣告式導航中,寫在程式設計式導航中更加合適
1 <Link to={{pathname:`/film/detail`,state={id:detailObj.id,title:detailObj.title}}}></Link>
  • 埋點資料
  • 在落地元件中通過this.props.location.state得到

接收示例:

 1 constructor(props){
 2   super(props)
 3   this.state = {
 4     // 接收動態路由引數
 5     news_id: this.props.match.params.id,
 6     // 接收查詢字串並處理
 7     query: querystring.parse(this.props.location.search.slice(1)),
 8     // 接收state
 9     state: this.props.location.state
10   };
11 }

七、路由三種渲染方式

1、component屬性(元件物件/函式)

1 <Route path="/home" component={Home} />
1 <Route path="/home" component={() => <Home />} />

2、render屬性(函式)

1 <Route path="/home" render={props => <Home />} />

3、children屬性(元件/函式

1 <Route path="/about" children={props => {
2     if(props.match){
3         return <div>children渲染</div>
4     }
5 }} />
1 <Route path="/about" children={<About />} />

注意

  • component可以使用元件類渲染或內聯方式渲染,render只能使用函式,children使用函式或直接使用元件
  • 當children的值是一個函式時,無論當前地址和path路徑匹不匹配,都將會執行children對應的函式,當children的值為一個元件時,當前地址和path不匹配時,路由元件不渲染
  • children函式方式渲染,會在形參中接受到一個物件,物件中match屬性如果當前地址匹配成功返回物件,否則null
  • children函式方式渲染,在自定義導航元件的上面會非常好用

八、withRouter高階元件

作用:把不是通過路由切換過來的元件中,將react-router 的 history、location、match 三個物件傳入props物件上

  • 預設情況下,必須是經過路由匹配渲染的元件才存在this.props才擁有路由引數,才能使用程式設計式導航的寫法,才能執行this.props.history.push('/uri')跳轉到對應路由的頁面
  • 然而不是所有元件都直接與路由相連的,當這些元件需要路由引數時,使用withRouter就可以給此元件傳入路由引數,此時就可以使用this.props
1 // 引入withRouter
2 import { withRouter} from 'react-router-dom'
3 
4 // 執行一下withRouter
5 export default withRouter(Cmp)

該高階元件是路由包自帶的東西,因此只需要引入+使用就可以了,不需要自己定義。

九、自定義導航元件

在專案中往往不是所有的宣告式導航都是需要a標籤完成,有時候可能需要別的標籤

此時如果在需要的地方去寫程式設計式導航就會有程式碼重複可能性,就在對於公共程式碼進行提取

思路:

  • 定義一個普通元件可以是類元件也可以是函式式元件
  • 父元件能向子元件傳值props
  • 不管路由規則是否匹配都要顯示元件Route,children渲染方式(函式式)
  • 注意點:react中元件是大寫字母開頭,html標籤也是元件

宣告式導航參考

 1 import React, { Component, Fragment } from "react";
 2 import { Route } from "react-router-dom"
 3 // 引入自定義導航元件
 4 import MyLink from "./Components/MyLink"
 5 import Cmp1 from "./Components/Cmp1";
 6 import Cmp2 from "./Components/Cmp2";
 7 
 8 class App extends Component {
 9   render() {
10     return (
11       <Fragment>
12         <MyLink tag="h1" to="/cmp1">
13           去1
14         </MyLink>
15         <MyLink tag="h1" to="/cmp2">
16           去2
17         </MyLink>
18         <Route path="/cmp1" component={Cmp1}></Route>
19         <Route path="/cmp2" component={Cmp2}></Route>
20       </Fragment>
21     );
22   }
23 }
24 export default App;

自定義導航元件參考

 1 import React, { Component, Fragment } from "react";
 2 import { withRouter, Route } from "react-router-dom";
 3 
 4 class MyLink extends Component {
 5   // 點選跳轉動作
 6   goUrl = () => {
 7     this.props.history.push(this.props.to);
 8   };
 9   render() {
10     // 獲取引數
11     var Tag = this.props.tag ? this.props.tag : "a";
12     return (
13       <Fragment>
14         <Route
15           path={this.props.to}
16           children={({ match }) => {
17             if (match) {
18               // 匹配
19               return (
20                 <Tag
21                   onClick={this.goUrl}
22                   style={{ color: "red" }}
23                   >
24                   {this.props.children}
25                 </Tag>
26               );
27             } else {
28               // 不匹配
29               return (
30                 <Tag onClick={this.goUrl}>
31                   {this.props.children}
32                 </Tag>
33               );
34             }
35           }}>
36         </Route>
37       </Fragment>
38     );
39   }
40 }
41 
42 export default withRouter(MyLink);

十、樣式引入問題

css樣式引入:public檔案下自定義樣式引入

  • 不要寫相對路徑,例如./css/index.css寫成/css/index.css
  • 路徑寫成href="%PUBLIC_URL%/css/index.css"
  • 導航方式:BrowserRouter改成HashRouter