前言
作爲 React
全家桶的一員,如果我們想要開發一個 React
應用,那麼 react-router
基本上是我們繞不過去的基礎。基於此,對它的瞭解和使用也是必不可少的一步
本文將重點介紹實際應用中常用的一些 API
以及實踐過程中遇到的一些問題,目標很簡單:會用
基於react-router
v5.0.1
,WEB
應用程序
安裝
國際慣例,首先我們需要安裝
npm install --save react-router-dom
從這一步開始,已經有同學有疑問了:我們明明在說 react-router
怎麼要下載安裝 react-router-dom
呢
React Router
現在已經被劃分成了三個包:react-router
,react-router-dom
,react-router-native
react-router
爲React Router
應用提供了核心的路由組件和函數,另外兩個包提供了特定環境的組件(瀏覽器和react-native
對應的平臺),不過他們也是將react-router
導出的模塊再次導出
因爲我們需要開發一個 web
應用,所以直接安裝 react-router-dom
就可以了
初體驗
首先,讓我們通過一個小小的示例來感知一下 react-router
import React from 'react'
import ReactDom from 'react-dom'
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom'
const Index = () => <div>Index頁面</div>
const About = () => <div>About頁面</div>
const App = () => {
return (
<BrowserRouter>
<div>
<div>
<ul>
<li><Link to='/'>Index</Link></li>
<li><Link to='/about'>About</Link></li>
</ul>
</div>
<div>
<Switch>
<Route path='/' exact component={Index}></Route>
<Route path='/about' exact component={About}></Route>
</Switch>
</div>
</div>
</BrowserRouter>
)
}
ReactDom.render(<App />, document.getElementById('app'))
通過上面的代碼我們已經實現了路由的基本功能,匹配不同的路徑,渲染不同的組件。下面,我們就上面的示例,認識一些 react-router
中高頻率出現的概念
常用組件
Router 組件
React Router
應用程序的核心,每個 router
都會創建一個 history
對象,用來保持對當前位置的追蹤
在 web
項目中,react-router-dom
提供了 BrowserRouter
和 HashRouter
路由。這兩個路由都會爲你創建一個專門的 history
對象。至於使用場景,一般情況下如果我們使用的是一個非靜態的站點、要處理不同的 url
就使用 BrowserRouter
,相反如果只處理靜態的 url
,則使用 HashRouter
Route 組件
Route
組件的主要職責:當鏈接符合匹配規則時,渲染組件
路由匹配是通過比較 Route
的 path
屬性和當前地址的 pathname
來實現的。當一個 Route
匹配成功時,它將渲染其內容,當它不匹配時就會渲染 null
。沒有路徑的 Route
將始終被匹配
Route
組件常用屬性:
-
path
: 字符串類型,用來匹配ulr
-
exact
:boolean
類型,如果爲true
,則只有在路徑完全匹配location.pathname
時才匹配 -
component
: 只有當位置匹配時纔會渲染的React
組件
注意點:
Route
組件屬性都不是必須的,如果缺少path
屬性,那麼將會匹配到任意url
<Route path='/' exact component={Index} />
<Route component={About} />
使用render
或者children
屬性可以替代component
屬性,由於render
與children
都是函數的形式,所以可以在它們當中做一些比較複雜的邏輯
render
函數也是在匹配url
的時候渲染,而children
函數 任何時候 都渲染,當路由匹配的時候match
是一個對象,否則爲null
當三者一起使用的時候,優先級爲
children
>component
>render
<!-- 使用 render 屬性 -->
<Route path='/about' render={() => <div>這個是render渲染的about頁面</div>} />
<Route path='/about' render={(props) => <About {...props} />} />
<!-- 使用 children 屬性 -->
<Route path='/about' children={() => <div>這是一個children渲染的about頁面</div>}
<Route path='/about' children={({match}) => match ? <div>1</div> : <div>2</div> }
Switch 組件
渲染與該地址匹配的第一個子節點Route
或者Redirect
這個組件最重要的作用是可以將 Route
組件分組
<Switch>
<Route path='/user' render={()=><div>user頁面</div>} />
<Route path='/:id' render={()=><div>子成員</div>} />
<Route render={()=><div>about頁面</div>} />
</Switch>
在上面這個示例中,在沒有 Switch
組件包裹的情況下,如果 ulr
是 /user
,那麼三個頁面將會全部匹配到。這樣的設計在一定程度上給我們提供了便利,比如說一個公共頁面需要渲染好幾個組件的情況。但是有時候我們並不想訪問到全部的匹配組件,這個時候就可以將這些 Route
組件使用 Switch
包裹起來,它將永遠渲染符合匹配項的第一個組件
注意點:
-
Switch
匹配的規則是同一個組中渲染第一個匹配組件,也就是說如果是包裹在兩個不同的Switch
組件中的,會分別渲染匹配到的第一個組件 -
Switch
組件中不能嵌套內置標籤元素,比如div
span
,但是可以嵌套組件,甚至可以添加path
屬性進行匹配, 實際上Route
本身就是組件,但是建議還是隻嵌套Route
或者Redirect
組件
Link 與 NavLink
相信小夥伴們看過前面的示例之後,應該對 Link
不會陌生了。它的作用就是提供聲明式的可訪問導航
Link
常用屬性:
-
to
:可以是String
類型或者具有pathname
、search
、hash
、state
任何屬性的對象pathname
: 表示要鏈接到的路徑的字符串search
: 表示查詢參數的字符串形式hash
: 放入網址的hash
state
: 狀態持續到location
-
replace
:boolean
類型,如果爲true
,點擊鏈接將替換當前歷史記錄
NavLink
一個特殊版本的Link
,當它與當前URL
匹配時,爲其渲染元素添加樣式屬性,其用法與Link
基本相同
注意點:
- 給渲染元素添加屬性可以使用
activeClassName
或者activeStyle
屬性進行添加,簡單來說就是使用class
類或者行內樣式 -
NavLink
有一個exact
屬性,如果爲true
,則僅在位置完全匹配時才應用active
的類/樣式
Redirect 組件
顧名思義,重定向組件,組件中的 to
屬性是必須的
屬性:
-
to
:string
類型或者一個對象(pathname
屬性是重定向到的URL
) -
push
:boolean
類型,當true
時,重定向會將新地址推入history
中,而不是替換當前地址,就是通過history.push
或者history.replace
實現 -
from
:重定向from
的路徑名,簡單說就是將要進入的url
-
exact
:完全匹配from
;相當於Route.exact
這個組件在一些場景中有很好的效果,比如我們登錄場景,前面我們介紹過的 Route
組件渲染屬性使用 render
或者 children
的時候,就完全可以根據判斷條件執行不同的路由跳轉
<!-- 官網示例代碼 -->
<Route exact path='/' render={()=>(
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)} />
<!--或者-->
<Route path='/about' children={({match})=>(
match ? (
<About />
) : (
<User />
)
)} />
前面介紹 Switch
組件時,有過它的身影,實際上它可以和 Switch
組件很好的配合,比如:
<Switch>
<Redirect from='/user' to='/about' />
<Route path='/user' render={()=><div>User頁面</div>} />
</Switch>
需要注意的地方:
-
from
屬性只能用於在Redirect
內部渲染Switch
時匹配地址 -
Redirect
組件中from
匹配的Route
要在前面定義
<!--錯誤姿勢,這樣是沒有效果的-->
<Route path='/user' render={()=><div>User頁面</div>} />
<Redirect from='/user' to='/about' />
withRouter
默認情況下,經過路由匹配的組件才擁有路由參數,我們就可以在其中使用 編程式導航,比如:
this.props.history.push('/about')
然而,不是所有的組件都是與路由相連的,比如直接在瀏覽器輸入地址打開的。這個時候我們訪問組件 props
的時候,它是一個空對象,就沒辦法訪問 props
中的 history
、match
、location
等對象
所以 這個時候 withRouter
閃亮登場
withRouter
的用法很簡單:
import React, { Component } from 'react';
<!--引入-->
import { Route, Link, Switch, withRouter } from 'react-router-dom'
class App extends Component {
render() {
<!-- 沒有使用 withRouter 的時候,是一個空對象-->
console.log(this.props)
return (
<div>
<Link to='/'>Index</Link>
<Link to='/about'>About</Link>
<Switch>
<Route path='/' exact render={()=> <div>Index頁面</div>} />
<Route path='/about' render={()=> <div>About頁面</div>} />
</Switch>
</div>
);
}
}
<!--執行-->
export default withRouter(App)
當然,還有很多種使用方式,比如通過 withRouter
監聽 loaction
對象改變文檔標題或者配合 redux
使用等等
詳見 示例demo
進一步
瞭解了 react-router
的這些基本知識點,貌似我們已經可以寫出來一個用路由搭建的項目了。但是,請暫時停下腳步想一下:在一個項目當中,如果我們遇到嵌套的路由呢、動態參數的路由呢?當然,只用前面瞭解到的東西,完全可以寫出來,但那是在是太 low
了
match 對象
在解答前面的兩個問題之前,我們需要先了解一個 match
對象
相信在前面的 withRouter
模塊,小夥伴們已經知道了 match
對象的存在,在動態路由和路由嵌套時,我們會經常和它打交道
一個match
對象中包涵了有關如何匹配URL
的信息
它包含以下屬性:
-
params
:與動態路徑的URL
對應解析,它裏面包含了動態路由裏面的信息 -
path
:用於匹配的路徑模式 -
url
:用於匹配部分的URL
-
isExact
- 如果爲true
匹配整個URL
(沒有結尾字符)
注意點:
- 如果
Route
沒有path
,那麼將會一直與他最近的父級匹配。這也同樣適用於withRouter
-
match
對象中的url
和path
,簡單來說path
是匹配的規則,url
則是實際匹配到的路徑
動態路由
瞭解了 match 對象,動態路由的定義其實很簡單
<Link to='/video/1'>視頻教程1</Link>
<Link to='/video/2'>視頻教程2</Link>
<Route path='/video/:id' component={Video} />
Route
組件可以匹配到 Link
鏈接跳轉的路徑,然後再 match
對象的 params
屬性中就可以拿到動態數據的具體信息
嵌套路由
React Router 4
不再提倡中心化路由,取之的是路由存在於佈局和 UI
之間,Route
本身就是一個組件
實現路由嵌套最簡單的方式:
<!--父組件-->
<Route path='/workplace' component={Workplace} />
<!--子組件-->
<Route path='/workplace/money' component={Money} />
當然,使用 match
來進行匹配會更加優雅
<!--父組件-->
<Route path='/video' component={Video} />
<!--子組件-->
<Route path={`${this.props.match.url}/react`} component={ReactVideo} />
注意點:
- 使用嵌套路由父級不能使用
exact
使用這樣的方式來配置路由規則,我們就只需要考慮 component
的渲染時機就可以了。但是,同樣的也會給我們帶來一些問題,比如說路由規則不是很直觀,尤其是對於寫過 vue
的小夥伴來說,要是有一個像配置 vue-router
規則的東西就好了。這個時候,我們可以試着去了解一個這個東西了 react-router-config
詳見 示例demo
後記
突如其來的結束語
關於 react-router
的基本用法就是想上面介紹的那樣,但是想要探究更多有意思或者更優雅的用法,還需要我們在具體的項目中去磨練,比如說路由的拆分、按需加載等等一系列東西
如果你也對 React
中的其他內容感興趣,想要了解更多前端片段,可以 點擊這裏 ,歡迎 star
關注