React系列(五)——React路由

前言

路由是React項(xiàng)目中相當(dāng)重要的概念覆获,對(duì)于功能較為復(fù)雜的網(wǎng)頁(yè)來(lái)說(shuō)渔彰,必然會(huì)涉及到不同功能間的頁(yè)面跳轉(zhuǎn)欧芽,本篇文章將對(duì)React官方維護(hù)的路由庫(kù)React-Router-Dom的使用和常用組件進(jìn)行講解症脂,同時(shí)對(duì)路由組件傳遞param參數(shù)的方式進(jìn)行講解织鲸,希望對(duì)各位讀者有所參考句占。

一沪摄、了解SAP和路由的概念

SAP(single page web application)的意思是單頁(yè)Web應(yīng)用,正如前言所說(shuō)纱烘,一般來(lái)說(shuō)功能較為復(fù)雜都會(huì)涉及到頁(yè)面跳轉(zhuǎn)的功能杨拐,而傳統(tǒng)的前端頁(yè)面跳轉(zhuǎn)往往是利用<a/>標(biāo)簽進(jìn)行跳轉(zhuǎn),這種方式雖然可以實(shí)現(xiàn)功能擂啥,但是每次跳轉(zhuǎn)到新的頁(yè)面都會(huì)重新對(duì)頁(yè)面的元素進(jìn)行加載哄陶,這樣其實(shí)對(duì)于用戶來(lái)說(shuō)是不太友好的。而單頁(yè)Web應(yīng)用則較好的解決了這個(gè)問(wèn)題哺壶,因?yàn)镾AP整個(gè)應(yīng)用都是在一個(gè)頁(yè)面上進(jìn)行的屋吨,每次的頁(yè)面跳轉(zhuǎn)只涉及到頁(yè)面中對(duì)應(yīng)組件(模塊)的更新操作,這樣就在一定程度上讓頁(yè)面不需要加載重復(fù)的頁(yè)面元素山宾。

再說(shuō)說(shuō)路由

路由其實(shí)可以理解為是一個(gè)映射關(guān)系至扰,即路徑到組件或者函數(shù)的對(duì)應(yīng)關(guān)系,比如說(shuō)/home這個(gè)路徑對(duì)應(yīng)著Home這個(gè)首頁(yè)組件塌碌,在React中渊胸,有react-router-dom這個(gè)官方維護(hù)的組件庫(kù)來(lái)幫助我們處理項(xiàng)目中的路由問(wèn)題,需要注意的是台妆,我們用create-react-dom創(chuàng)建的react項(xiàng)目翎猛,默認(rèn)是沒有react-router-dom的,所以需要我們自己再額外下載到項(xiàng)目中接剩。

二切厘、路由入門小案例

當(dāng)前存在HomeAbout兩個(gè)組件懊缺,我們希望在頁(yè)面上的導(dǎo)航欄中實(shí)現(xiàn)點(diǎn)擊對(duì)應(yīng)標(biāo)簽疫稿,即可在頁(yè)面上顯示對(duì)應(yīng)組件的內(nèi)容培他。

Home組件:
import React, { Component } from 'react'
export default class Home extends Component {
    render() {
        return (
            <h3>我是Home的內(nèi)容</h3>
        )
    }
}
About組件:
import React, { Component } from 'react'
export default class About extends Component {
    render() {
        return (
            <h3>我是About的內(nèi)容</h3>
        )
    }
}
App組件
省略import...

export default class App extends Component {
    render() {
        return (
            <div>
                <div class="row">
                    <div class="col-xs-offset-2 col-xs-8">
                        <div class="page-header"><h2>React Router Demo</h2></div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-xs-2 col-xs-offset-2">
                        <div class="list-group">
                            <a class="list-group-item" to='/about'>About</a>
                            <a class="list-group-item" to='/home'>Home</a>
                        </div>
                    </div>
                    <div class="col-xs-6">
                        <div class="panel">
                            <div class="panel-body">
                              
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
步驟一:下載路由組件庫(kù)
install i react-router-dom
步驟二:引用Link標(biāo)簽,替代<a>標(biāo)簽進(jìn)行跳轉(zhuǎn):

需要注意的是<Link>其實(shí)本質(zhì)上也是<a>標(biāo)簽遗座,只是說(shuō)它阻止了a標(biāo)簽?zāi)J(rèn)的跳轉(zhuǎn)動(dòng)作舀凛,但保留了其修改瀏覽器URL路徑的能力。

import {Link,Router, Route} from 'react-router-dom'
...
export default class App extends Component {
    render() {
        return (
            <div>
                <div class="row">
                    <div class="col-xs-offset-2 col-xs-8">
                        <div class="page-header"><h2>React Router Demo</h2></div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-xs-2 col-xs-offset-2">
                        <div class="list-group">
                            {/* 
                            使用Link標(biāo)簽代替 a 標(biāo)簽途蒋,這一步是用于實(shí)現(xiàn)第一步:瀏覽器URL的變更
                            */}
                            <Link class="list-group-item" to='/about'>About</Link>
                            <Link class="list-group-item" to='/home'>Home</Link>
                        </div>
                    </div>
                    <div class="col-xs-6">
                        <div class="panel">
                            <div class="panel-body">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
步驟三:定義路由對(duì)應(yīng)要跳轉(zhuǎn)的組件:

這一步也比較好理解猛遍,前端路由器(會(huì)在步驟四配置)會(huì)自動(dòng)檢測(cè)到瀏覽器的url發(fā)生了變化,此時(shí)就會(huì)拿新的url地址到路由表中進(jìn)行匹配号坡,步驟三定義的就是和url匹配成功的路由懊烤,將顯示對(duì)應(yīng)哪個(gè)組件。

import {Link,Router, Route} from 'react-router-dom';

export default class App extends Component {
    render() {
        return (
            <div>
                <div class="row">
                    <div class="col-xs-offset-2 col-xs-8">
                        <div class="page-header"><h2>React Router Demo</h2></div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-xs-2 col-xs-offset-2">
                        <div class="list-group">
                            {/* 
                            使用Link標(biāo)簽代替 a 標(biāo)簽宽堆,這一步是用于實(shí)現(xiàn)第一步:瀏覽器URL的變更
                            */}
                            <Link class="list-group-item" to='/about'>About</Link>
                            <Link class="list-group-item" to='/home'>Home</Link>
                        </div>
                    </div>
                    <div class="col-xs-6">
                        <div class="panel">
                            <div class="panel-body">
                                {/* 
                                在App.js中腌紧,定義路由,路由規(guī)定了path對(duì)應(yīng)跳轉(zhuǎn)的組件
                                */}
                                <Route path="/about" component={About}/>
                                <Route path="/home" component={Home}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
步驟四:在最外層的index.js中配置前端路由器

這里可以使用Router組件畜隶,但我們一般是使用功能更為強(qiáng)大的BrowerRouter或者HashRouter壁肋。

import React from 'react'
import ReactDOM from 'react-dom'
// 注意,我們一般不直接引入Router代箭,而是引入 BrowserRouter 或者 HashRouter
import {BrowserRouter} from 'react-router-dom'
import App from './App'

ReactDOM.render(<BrowserRouter><App/></BrowserRouter>,document.getElementById('root'))
入門案例效果圖

最后墩划,我們?cè)賹?duì)React路由實(shí)現(xiàn)的原理做一個(gè)小結(jié):

(1)使用<Link>等標(biāo)簽實(shí)現(xiàn)對(duì)瀏覽器path的操作(本質(zhì)上是對(duì)BOM對(duì)象的history進(jìn)行操作)
(2)當(dāng)前端路由器檢測(cè)到瀏覽器的path發(fā)生了變化涕刚,就會(huì)到路由中對(duì)新的路徑進(jìn)行匹配

三嗡综、路由的常用組件

(一)NavLink標(biāo)簽

NavLink組件是在Link組件的基礎(chǔ)上做了高亮特效的增強(qiáng),在我們快速入門的案例中杜漠,我們想要增加一個(gè)功能极景,當(dāng)我點(diǎn)擊導(dǎo)航欄的某個(gè)標(biāo)簽時(shí),對(duì)應(yīng)的標(biāo)簽要有高亮顯示的效果驾茴。比較容易地我們會(huì)想到給每個(gè)標(biāo)簽增加一個(gè)是否高亮的flag盼樟,當(dāng)滿足選中的條件時(shí),即將高亮的樣式追加到頁(yè)面的標(biāo)簽上锈至。
而NavLink組件可以說(shuō)是幫我們簡(jiǎn)化了上述的操作晨缴,我們只需要傳入activeClassName指定具體追加的屬性即可。需要注意的是峡捡,如果追加的高亮屬性命名是 active時(shí)击碗,可以省略。
上一小節(jié)的案例可以優(yōu)化為:

import {NavLink,Route} from 'react-router-dom'们拙; 
export default class App extends Component {
    render() {
        return (
            <div>
               ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <NavLink activeClassName="active" className="list-group-item" to='/about'>About</NavLink>
                            <NavLink activeClassName="active" className="list-group-item" to='/home'>Home</NavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Route path="/about" component={About}/>
                                <Route path="/home" component={Home}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
NavLink效果演示
(二)Switch組件

當(dāng)路由中出現(xiàn)了2個(gè)或者2個(gè)以上的path同時(shí)匹配的情況稍途,那么實(shí)際上對(duì)應(yīng)的路由組件都會(huì)被渲染。如果我們想要說(shuō)只渲染(掛載)第一個(gè)匹配上的組件的話砚婆,那么我們可以使用<Switch>組件來(lái)解決械拍。

我們先來(lái)看一下未使用Switch組件的情況: /home對(duì)應(yīng)著2個(gè)組件

export default class App extends Component {
    render() {
        return (
            <div>
              ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Route path="/about" component={About}/>
                                <Route path="/home" component={Home}/>
                                <Route path="/home" component={Home2}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
未使用switch標(biāo)簽之前

我們可以引入Switch組件來(lái)解決這個(gè)問(wèn)題:

export default class App extends Component {
    render() {
        return (
              ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Switch>
                                <Route path="/about" component={About}/>
                                <Route path="/home" component={Home}/>
                                <Route path="/home" component={Home2}/>
                                </Switch>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
使用Switch組件后的演示結(jié)果
(三)Redirect組件

我們?cè)谶M(jìn)入網(wǎng)站的時(shí)候,網(wǎng)站的導(dǎo)航欄中往往會(huì)有一個(gè)默認(rèn)選中的標(biāo)簽,對(duì)于這種場(chǎng)景坷虑,React路由其實(shí)也為我們提供了對(duì)應(yīng)的組件來(lái)幫助我們簡(jiǎn)化開發(fā)甲馋,那就是Redirect組件。我們?cè)诳焖偃腴T的案例中新增加一個(gè)功能迄损,進(jìn)入首頁(yè)后摔刁,默認(rèn)跳轉(zhuǎn)到/about路徑下。

export default class App extends Component {
    render() {
        return (
            <div>
              ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Switch>
                                <Route  path="/about" component={About}/>
                                <Route  path="/home" component={Home}/>
                                <Redirect to="/about" />
                                </Switch>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
Redirect組件演示結(jié)果

四海蔽、精準(zhǔn)匹配和模糊匹配

React路由的匹配方式一共有2種共屈,分別是精準(zhǔn)匹配和模糊匹配。
我們知道党窜,要想正確使用react-router-dom來(lái)實(shí)現(xiàn)路由跳轉(zhuǎn)拗引,一共需要更新path和匹配path對(duì)應(yīng)組件這兩步。
精準(zhǔn)匹配很好理解幌衣,就是path的路徑和路由的路徑必須完全相同才能匹配成功矾削。
而模糊匹配,則是只要path能夠覆蓋route的路徑豁护,不需要完全匹配即可匹配成功哼凯。當(dāng) Link標(biāo)簽中定義的path為二級(jí)(或以上級(jí)別)的路由,那么只需要其第一級(jí)的path和Route定義的path一致即可匹配成功楚里;反之断部,如果是Link表中定義的path只是一級(jí)路由,而Route不存在完全匹配的路由班缎,則匹配失敗蝴光。 舉例來(lái)說(shuō),<Link to="/home/a/b"> <Route path="/home" component={Xxxx} /> 在模糊匹配的條件下达址,是可以成立的蔑祟。而 <Link to="/home"> <Route path="/home/a/b" component={Xxxx} /> 這種情況下是不可以匹配成功的。
我們可以通過(guò)下面的例子來(lái)加深一下對(duì)精準(zhǔn)匹配和模糊匹配的理解:

(一)精準(zhǔn)匹配

常規(guī)的精準(zhǔn)匹配要求沉唠,跳轉(zhuǎn)的路徑和路由中配置的路徑要完全匹配疆虚。由于路由默認(rèn)是模糊匹配,如果需要開啟精準(zhǔn)匹配满葛,需要我們?cè)?code>Route組件中配置exact=true或者直接簡(jiǎn)寫exact径簿。精準(zhǔn)匹配使用簡(jiǎn)單,但實(shí)際上纱扭,exact 在實(shí)際應(yīng)用中還是盡量要謹(jǐn)慎使用牍帚,避免出現(xiàn)二級(jí)(n級(jí))路由匹配不到的問(wèn)題出現(xiàn)。(詳見下一節(jié)的嵌套路由)

export default class App extends Component {
    render() {
        return (
            <div>
              ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Route  exact  path="/about" component={About}/>
                                <Route exact   path="/home" component={Home}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
(二)模糊匹配

在下面的案例中乳蛾,<MyNavLink>組件(自己對(duì)NavLink組件的封裝)中配置的路徑為/about/test/home/test暗赶,和路由配置的path并不完全一致鄙币。(屬于前者為后者的子集),此時(shí)是可以正常跳轉(zhuǎn)的蹂随。

export default class App extends Component {    render() {
        return (
            <div>  
            ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about/test">About</MyNavLink>
                            <MyNavLink to="/home/test">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Route  path="/about" component={About}/>
                                <Route path="/home" component={Home}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
模糊匹配演示用例1

我們可以嘗試著換種方式十嘿,改成<MyNavLink>配置的路徑是<Route>組件配置路徑的父集,看組件能夠跳轉(zhuǎn)成功岳锁。

export default class App extends Component {    render() {
        return (
            <div>  
            ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Route  path="/about/test" component={About}/>
                                <Route path="/home/test" component={Home}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

模糊匹配演示用例2

我們可以從上圖中看到绩衷,此時(shí)About組件的內(nèi)容是沒有出來(lái)的,也就是說(shuō)模糊匹配并不支持瀏覽器跳轉(zhuǎn)的url是<Route>組件配置路徑的父集這個(gè)情景激率。上述這種情況其實(shí)不難理解咳燕,我們真正去匹配一個(gè)組件時(shí),應(yīng)該是以Route配置為準(zhǔn)乒躺,從父組件(一級(jí)路由)一步一步地匹配到子組件(n級(jí)路由)的招盲。反之,一級(jí)路由下往往可能存在有多個(gè)子路由的可能嘉冒,此時(shí)就必然會(huì)面臨頁(yè)面不知道選擇哪個(gè)子路由作為跳轉(zhuǎn)組件的問(wèn)題曹货。

五、嵌套(多級(jí))路由

隨著組件的復(fù)雜度組件上升讳推,有時(shí)候我們可能會(huì)在組件中嵌套多級(jí)的路由顶籽,現(xiàn)在我們?cè)谏弦恍」?jié)的基礎(chǔ)上,新增一個(gè)需求:在Home組件中中新增2個(gè)組件银觅,并對(duì)2個(gè)組件配置對(duì)應(yīng)的二級(jí)路由礼饱。
這里的話省略2個(gè)新組件的代碼(要的話可以從文章最后的碼云鏈接上面看),直接看App組件和Home組件是怎么配置路由的:

App組件
export default class App extends Component {    
render() {
        return (
            <div>
              ...
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Switch>
                                <Route  path="/about" component={About}/>
                                <Route  path="/home" component={Home}/>
                                </Switch>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
Home組件:
export default class Home extends Component {
    render() {
        return (
            <div>
                <h3>我是Home的內(nèi)容</h3>
                <ul class="nav nav-tabs">
                    <li><MyNavLink to="/home/news">News</MyNavLink></li>
                    <li><MyNavLink to="/home/messages">Message</MyNavLink></li>
                </ul>
                <Route path="/home/news" component={News} />
                <Route path="/home/messages" component={Message} />
            </div>
        )
    }
}
嵌套路由演示用例

我們可以看到设拟,在Home組件中慨仿,我們<NavLink>組件和<Route>組件對(duì)應(yīng)的路由都是二級(jí)路由久脯,前者很好理解纳胧,<NavLink>的配置的to屬性表示接下來(lái)瀏覽器將要跳轉(zhuǎn)的路徑,單純地寫/new的路徑實(shí)際的請(qǐng)求結(jié)果將為:localhost:8080/new帘撰,不會(huì)帶上上一級(jí)的路由跑慕。
而Home組件的Route雖然是嵌套在App組件中的路由中,但是對(duì)應(yīng)的一級(jí)路由還是必須寫的摧找。因?yàn)槁酚善髟谄ヅ涞臅r(shí)候核行,是根據(jù)路由注冊(cè)的層級(jí)/時(shí)機(jī)來(lái)逐層匹配的,也就是說(shuō)蹬耘,/news 會(huì)先去App.js中的路由中進(jìn)行匹配芝雪,如果匹配不到,那么是到達(dá)不了這個(gè)頁(yè)面的路由的综苔,所以這時(shí)惩系,就需要在Link標(biāo)簽中使用二級(jí)路由了位岔。
所以說(shuō),如果在父路由中使用了精準(zhǔn)匹配堡牡,那么對(duì)于子路由的請(qǐng)求基本上都會(huì)被攔截下來(lái)抒抬,使用不了的。

六晤柄、組件路由傳遞參數(shù)

在上面的講解中擦剑,我們知道了組件如何進(jìn)行跳轉(zhuǎn),但是如果我們需要組件在跳轉(zhuǎn)過(guò)程中攜帶參數(shù)到下一個(gè)組件中芥颈,我們可以怎么實(shí)現(xiàn)呢惠勒?常見的組件路由傳遞參數(shù)的方式有三種,分別是通過(guò)params參數(shù)傳遞爬坑、利用search屬性傳遞捉撮、利用state屬性傳遞,下面我們就來(lái)看一下這三種方式的具體使用吧妇垢。

(一)通過(guò)params進(jìn)行參數(shù)傳遞
步驟一:在<Link>組件上傳遞參數(shù)

一般來(lái)說(shuō)巾遭,我們會(huì)使用類似于/${id}的方式來(lái)傳遞值

<NavLink to={`/home/messages/details/${messageObj.id}/${messageObj.title}`}>xxx</NavLink>
步驟二:在路由上寫好每個(gè)屬性對(duì)應(yīng)匹配的值

步驟一中雖然傳遞了多個(gè)參數(shù),但是我們并不知道每個(gè)參數(shù)對(duì)應(yīng)的key是什么闯估,所以需要先規(guī)定好每個(gè)參數(shù)對(duì)應(yīng)的key灼舍。

<Route path={'/home/messages/details/:id/:title'} component={Details}/>
步驟三:在目標(biāo)組件中使用this.props.match來(lái)獲取傳遞的參數(shù)
export default class Details extends Component {
    state = {
        details: [
            {id:'01',comment:'你好,我的祖國(guó)'}
            ...
        ]
    }
    render() {
        const {details} = this.state;
        const {params} = this.props.match;
        const detailofMessage = details.find((detailObj)=>{
            return detailObj.id === params.id 
        })
        return (
            <ul>
                <li>ID: {detailofMessage.id}</li>
                <li>Title: {params.title}</li>
                <li>Comment: {detailofMessage.comment}</li>
            </ul>
        )
    }
}

為什么通過(guò)props可以獲取到參數(shù)呢涨薪?我們不妨打印一下this.props看一下此時(shí)的Details 組件到底接收了哪些信息骑素。
我們可以看到,此時(shí)組件的props屬性中刚夺,有著三個(gè)屬性献丑,分別是historylocation和match侠姑,而match`屬性中已經(jīng)幫我們把傳遞的參數(shù)封裝成為一個(gè)對(duì)象了创橄。

利用params傳遞參數(shù)演示用例

(二)組件路由通過(guò)search屬性傳遞參數(shù)

這種方式基本上和我們常見的在url地址上進(jìn)行參數(shù)拼接是一樣的,這種方式寫法較為簡(jiǎn)單莽红,只需要在 Link 標(biāo)簽中定義好key=value形式參數(shù)拼接妥畏,然后在實(shí)際的組件中,直接從prop屬性中獲取安吁,調(diào)用querystring(或者其他處理字符串轉(zhuǎn)對(duì)象的函數(shù)庫(kù))處理參數(shù)醉蚁,并封裝成對(duì)象返回即可

步驟一:在<Link>組件上實(shí)現(xiàn)參數(shù)的拼接
 <NavLink to={`/home/messages/details?id=${messageObj.id}&title=${messageObj.title}`}>xxx</NavLink>
步驟二:配置路由(其實(shí)這一步可以省略,因?yàn)檫@種方式下的傳參并不需要額外對(duì)路由進(jìn)行配置)
 <Route path={'/home/messages/details'} component={Details}/>
步驟三:在實(shí)際組件中獲取傳遞的參數(shù)

我們先來(lái)看一下此時(shí)目標(biāo)組件接收到的props參數(shù)是什么?

組件的this.props接收到的參數(shù)

我們可以發(fā)現(xiàn)鬼店,此時(shí)props.location.search屬性中把我們步驟一的參數(shù)都封裝了進(jìn)去网棍,但是這種方式封裝的參數(shù)是字符串形式的,我們并不能直接使用妇智,而是要借助queryString庫(kù)來(lái)幫助我們把字符串解析成對(duì)象滥玷。

import React, { Component } from 'react'
// querystring 是腳手架初始化的時(shí)候自帶的函數(shù)庫(kù)捌锭,能幫助我們分解和生成key=value形式的對(duì)象
import qs from 'querystring'
export default class Details extends Component {
    state = {
        details: [
            {id:'01',comment:'你好,我的祖國(guó)'}  
            ...
        ]
    }
    render() {
        console.log('this.props',this.props)
        const {details} = this.state;
        const {search} = this.props.location;
        // parse能將字符串轉(zhuǎn)為key:value形式的對(duì)象
        const messageObj = qs.parse(search.slice(1));
        const detailofMessage = details.find((detailObj)=>{
            return detailObj.id === messageObj.id 
        })
        return (
            <ul>
                <li>ID: {detailofMessage.id}</li>
                <li>Title: {messageObj.title}</li>
                <li>Comment: {detailofMessage.comment}</li>
            </ul>
        )
    }
}
(三)通過(guò)state屬性來(lái)進(jìn)行參數(shù)的傳遞

這種形式的參數(shù)傳遞很有意思罗捎,和前兩種傳遞參數(shù)的方式不同观谦,這種方式傳遞的參數(shù)是不會(huì)在瀏覽器中顯式的展示的,同時(shí)桨菜,即使刷新頁(yè)面豁状,路由子頁(yè)面的數(shù)據(jù)還是不會(huì)消失。原因是react路由器幫我們對(duì)維護(hù)了history對(duì)象(history對(duì)象中又維護(hù)了location對(duì)象,所以也就有了state對(duì)象)

步驟一:在<Link>組件上傳遞參數(shù)

需要注意的是倒得,和其他兩種方式不同泻红,這里的話to屬性并不是直接傳一個(gè)字符串,而是傳了一個(gè)對(duì)象霞掺,對(duì)象中包含了pathname以及state谊路。

<NavLink to={{pathname:'/home/messages/details',state:{id:messageObj.id,title:messageObj.title}}}>xxx</NavLink>
步驟二:配置路由(其實(shí)這一步可以省略,因?yàn)檫@種方式下的傳參并不需要額外對(duì)路由進(jìn)行配置)
<Route path={'/home/messages/details'} component={Details}/>
步驟三:在具體組件中獲取傳遞的參數(shù)

和之前一樣菩彬,我們先在目標(biāo)組件中將接受到的prpos進(jìn)行打印輸出缠劝,我們可以看到,此時(shí)在location.state屬性中骗灶,我們可以獲取到我們?cè)诓襟E一中傳遞的參數(shù)惨恭。

目標(biāo)組件輸出this.props的結(jié)果

export default class Details extends Component {
    state = {
        details: [
            {id:'01',comment:'你好,我的祖國(guó)'}
            ...
        ]
    }

    render() {
        const {details} = this.state;
        const {id,title} = this.props.location.state;
        const detailofMessage = details.find((detailObj)=>{
            return detailObj.id === id 
        })
        return (
            <ul>
                <li>ID: {detailofMessage.id}</li>
                <li>Title: {title}</li>
                <li>Comment: {detailofMessage.comment}</li>
            </ul>
        )
    }
}

七耙旦、push和replace跳轉(zhuǎn)和編程式路由

(一)push和replace跳轉(zhuǎn)的區(qū)別

我們知道脱羡,url跳轉(zhuǎn)方式可以有2種,一種是push(把本次操作記錄推入history的棧中)免都,還有一種是replace(把上一次操作記錄替換成本次操作記錄)锉罐。<Link>組件默認(rèn)使用的跳轉(zhuǎn)方式是push,如果想使用replase進(jìn)行跳轉(zhuǎn)绕娘,我們可以通過(guò)在組件上新增replace=true或者是使用簡(jiǎn)寫方式replace來(lái)進(jìn)行修改脓规。
我們?cè)谏弦恍」?jié)的基礎(chǔ)上,新增一個(gè)新的需求业舍,每次跳轉(zhuǎn)到Details組件時(shí)抖拦,都使用replace這種跳轉(zhuǎn)方式

<NavLink replace to={{pathname:'/home/messages/details',state:{id:messageObj.id,title:messageObj.title}}}>xxx</NavLink>

這個(gè)特性比較簡(jiǎn)單,這里就不使用圖來(lái)表示了...

(二)編程式路由

除了使用路由組件進(jìn)行跳轉(zhuǎn)之外舷暮,其實(shí)我們自己也可以利用事件處理函數(shù)來(lái)走路由跳轉(zhuǎn)和參數(shù)傳遞的功能。

go & goBack & goForward

在路由組件中噩茄,我們可以通過(guò)this.prop.history獲取到history對(duì)象下面,然后使用對(duì)象對(duì)應(yīng)的API實(shí)現(xiàn)(歷史)路徑的跳轉(zhuǎn)。簡(jiǎn)單理解的話绩聘,就是通過(guò)路由組件的history對(duì)象沥割,實(shí)現(xiàn)頁(yè)面前進(jìn)鞋邑、后退的功能枷邪。

export default class Message extends Component {
    state = {...}
    // 前進(jìn)+1
    forward = ()=>{
        this.props.history.goForward();
    }
    // 后退+1
    back = ()=>{
        this.props.history.goBack();
    }
    // 后退+2
    go = ()=>{
        this.props.history.go(-2);
    }
    render() {
        const { messages } = this.state;
        return (
            <div>
                ...
                <button onClick={this.forward}>前進(jìn)</button>
                &nbsp;<button onClick={this.back}>后退</button>
                <button onClick={this.go}>后退+2</button>
            </div>
        )
    }
}
通過(guò)history對(duì)象主動(dòng)調(diào)用push/replace方法

我們可以在路由組件中通過(guò)this.props.history獲取到history對(duì)象后,通過(guò)主動(dòng)調(diào)用push或者replace方法來(lái)進(jìn)行路由的跳轉(zhuǎn),需要注意的是陌宿,當(dāng)通過(guò)編程式事務(wù)主動(dòng)進(jìn)行路由跳轉(zhuǎn)時(shí),對(duì)應(yīng)的參數(shù)需要和之前一樣窑眯,根據(jù)傳遞方式的不同來(lái)定義:

export default class Message extends Component {

    state = {...}
 
    pushShow = (id,title)=>{
   方式1梭伐,采用 params 參數(shù)傳遞
    //    this.props.history.push(`/home/messages/details/${id}/${title}`)
   方式2, 采用 search 參數(shù)傳遞
    //    this.props.history.push(`/home/messages/details?id=${id}&title=${title}`)
    方式3,采用state 參數(shù)傳遞 (state中的id和title采用了簡(jiǎn)寫方式蚀苛,完整寫法應(yīng)該是id:id,title:title)
    this.props.history.push({pathname:'/home/messages/details',state:{id,title}})
    }

    replaceShow = (id,title) =>{
        方式1在验,采用 params 參數(shù)傳遞
        // this.props.history.replace(`/home/messages/details/${id}/${title}`)
        方式2: 采用 search 參數(shù)傳遞
        // this.props.history.replace(`/home/messages/details?id=${id}&title=${title}`)
       方式3,采用state 參數(shù)傳遞 
        this.props.history.replace({pathname:'/home/messages/details',state:{id,title}})
    }

  ...
    render() {
        const { messages } = this.state;
        return (
            <div>
                <div>
                    <ul>
                        {
                            messages.map((messageObj) => {
                                return (
                                    <li key={messageObj.id}>
                                         {/* 傳遞state參數(shù) */}
                                        <NavLink replace to={{ pathname: '/home/messages/details', state: { id: messageObj.id, title: messageObj.title } }}>{messageObj.title}</NavLink>
                                        {/* <NavLink replace to={`/home/messages/details/${messageObj.id}/${messageObj.title}`}>{messageObj.title}</NavLink> */}
                                        {/* <NavLink replace to={`/home/messages/details?id=${messageObj.id}&title=${messageObj.title}`}>{messageObj.title}</NavLink> */}
                                        &nbsp;<button onClick={()=>{this.pushShow(messageObj.id,messageObj.title)}}>push跳轉(zhuǎn)</button>
                                        &nbsp;<button onClick={()=>{this.replaceShow(messageObj.id,messageObj.title)}}>replace跳轉(zhuǎn)</button>
                                    </li>

                                )
                            })
                        }
                    </ul>
                </div>
                 <Route path={'/home/messages/details'} component={Details} />
                {/* <Route path={'/home/messages/details/:id/:title'} component={Details} /> */}
                ...
            </div>
        )
    }
}

八堵未、withRouter的使用

我們知道腋舌,在路由組件中,我們可以使用props攜帶的參數(shù)來(lái)實(shí)現(xiàn)路由的跳轉(zhuǎn)以及參數(shù)的獲取渗蟹,那么對(duì)于非路由組件來(lái)說(shuō)块饺,如果想要路由組件的功能的話,可以怎么實(shí)現(xiàn)呢雌芽?
針對(duì)這個(gè)需求刨沦,react-router-dom就提供了withRouter這個(gè)函數(shù),幫助我們實(shí)現(xiàn)把一般組件包裝為路由組件膘怕。下面的Header組件原本是一個(gè)一般組件想诅,我們通過(guò)使用withRouter函數(shù),把原先的Header組件作為參數(shù)傳入岛心,返回值就是一個(gè)包裝后的新組建

import React, { Component } from 'react';
import {withRouter} from 'react-router-dom'

 class Header extends Component {

    goForward = ()=>{
        this.props.history.goForward();
    }
    goBack = ()=>{
        this.props.history.goBack();
    }
    go = ()=>{
        this.props.history.go(2);
    }

    render() {
        return (
            <div className="col-xs-offset-2 col-xs-8">
                <div className="page-header"><h2>React Router Demo</h2></div>
                <button onClick={this.goForward}>前進(jìn)</button>
                &nbsp;<button onClick={this.goBack}>后退</button>
                &nbsp;<button onClick={this.go}>前進(jìn)+2</button>
            </div>
        )
    }
}

export default withRouter(Header)

說(shuō)在最后

實(shí)際上来破,React路由中的內(nèi)容還是比較多的,本篇文章只是把常用的組件和函數(shù)進(jìn)行講解而已忘古,更多的使用方法還是要在實(shí)際項(xiàng)目中具體去查看文檔加以使用徘禁。
文章中用到案例已經(jīng)放在了碼云上,有需要的可以自行下載:https://gitee.com/moutory/react-staging

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末髓堪,一起剝皮案震驚了整個(gè)濱河市送朱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌干旁,老刑警劉巖驶沼,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異争群,居然都是意外死亡回怜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門换薄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)玉雾,“玉大人翔试,你說(shuō)我怎么就攤上這事「囱” “怎么了垦缅?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驹碍。 經(jīng)常有香客問(wèn)我壁涎,道長(zhǎng),這世上最難降的妖魔是什么幸冻? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任粹庞,我火速辦了婚禮,結(jié)果婚禮上洽损,老公的妹妹穿的比我還像新娘庞溜。我一直安慰自己,他們只是感情好碑定,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布流码。 她就那樣靜靜地躺著,像睡著了一般延刘。 火紅的嫁衣襯著肌膚如雪漫试。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天碘赖,我揣著相機(jī)與錄音驾荣,去河邊找鬼。 笑死普泡,一個(gè)胖子當(dāng)著我的面吹牛播掷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撼班,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼歧匈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了砰嘁?” 一聲冷哼從身側(cè)響起件炉,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矮湘,沒想到半個(gè)月后斟冕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡板祝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年宫静,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片券时。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孤里,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橘洞,到底是詐尸還是另有隱情捌袜,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布炸枣,位于F島的核電站虏等,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏适肠。R本人自食惡果不足惜霍衫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侯养。 院中可真熱鬧敦跌,春花似錦、人聲如沸逛揩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辩稽。三九已至惧笛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逞泄,已是汗流浹背患整。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喷众,地道東北人各谚。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像侮腹,于是被迫代替她去往敵國(guó)和親嘲碧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容