React路由
react路由
現代的前端應用大多都是SPA(單頁應用程序),也就是只有一個HTML頁面的應用程序。因為它的用戶體驗更好、對服務器的壓力更小,所以更受歡迎。為了有效的使用單個頁面來管理原來多頁面的功能,前端路由應運而生
前端路由的功能:讓用戶從一個視圖(頁面)導航到另一個視圖(頁面),前端路由是一套映射規則,在Reat中是URL路徑與組件的對應關系,使用 React路由簡單來說,就是配置路徑和組件(配對)。
?
文檔:https://react-router.docschina.org/web/guides/philosophy
?
react路由的基本使用
- 安裝:
npm i react-router-dom - 導入路由的三個核心組件,它們是組件
import {
BrowserRouter as Router, Route, Link } from "react-router-dom";
- 使用Router組件包裹整個應用
- 使用Link組件作為導航菜單(路由入口)
- 使用Route組件配置路由規則和要展示的組件(路由出口)
import React from 'react'
import ReactDom from 'react-dom'
// 導入路由組件
import {
BrowserRouter as Router, Route, Link } from "react-router-dom";
const App = () => (
// 使用Router組件包裹整個應用
<Router>
<div>
<h1>react</h1>
{
/* 指定路由入口 */}
<Link to="/first">頁面1</Link>
<Link to="/second">頁面2</Link>
{
/* 指定路由出口,path設置為Link中的to屬性,component設置為要渲染的組件 */}
<Route path="/first" component={
First}></Route>
<Route path="/second" component={
Second}></Route>
</div>
</Router>
)
class First extends React.Component {
render() {
return (
<div>
<h2>我是頁面1的標題</h2>
</div>
)
}
}
const Second = () => (
<div>
<h2>我是頁面2</h2>
</div>
)
ReactDom.render(<App />, document.getElementById('root'));
常用組件說明
BrowserRouter和HashRouter組件
- BrowserRouter或HashRouter組件:包裹整個應用,一個 React應用只需要使用一次
- HashRouter:使用URL的哈希值實現( localhost:3000/#/first)
- BrowserRouter:使用H5的 history Api實現( localhost:3000/first)
import {
BrowserRouter, Route, Link } from "react-router-dom";
import {
HashRouter, Route, Link } from "react-router-dom";
hash模式下#后邊的路徑不會發給服務器,不會被包括在 HTTP 請求中,對后端完全沒有影響,因此改變 hash 不會重新加載頁面,在處理相對路徑方面的一些問題時,使用HashRouter可以解決。
?
Link組件
Link組件:用于指定導航鏈接,默認會被渲染為一個a標簽,Link組件的to屬性會作為href值
<Link to="/first">頁面1</Link>
<a href="/first">頁面1</a>
Route組件
Route組件:指定路由展示的組件(注冊路由)
- path屬性:路由規則
- component屬性:指定當路由匹配時要展示的組件
- Route組件寫在哪,渲染出來的組件就展示在哪
<Route path="/first" component={
First}></Route>
如果沒有path屬性,將匹配所有的路徑。
?
NavLink組件
NavLink可以實現路由鏈接的高亮,通過activeClassName指定樣式名。當點擊哪個導航鏈接,哪個導航菜單就會應用activeClassName指定的樣式。
?
對NavLink再做一層封裝
import React, {
Component } from 'react'
import {
NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
console.log(this.props)
return <NavLink activeClassName="active" {
...this.props}></NavLink>
}
}
使用
<MyNavLink to="/home" children="home" />
<MyNavLink to="/about" children="about" />
Switch組件
默認情況下,在匹配到一個路由后會繼續往下匹配。比如下方代碼,在/home路徑匹配到Home組件的情況下,依然會繼續往下匹配到Test組件
但是一個路由一般只對應一個組件,在已經匹配到的情況下就沒有必要繼續往下匹配了。此時可以使用Switch組件,Switch可以提高路由匹配效率(單一匹配)。
import {
Route, Switch } from 'react-router-dom'
Routes組件
注意?。?!在 react-router-dom的6.x版本中,“Switch”被替換為了“Routes”,需要更新導入語句
import {
Switch, Route } from "react-router-dom";
// 更新為
import {
Routes ,Route } from 'react-router-dom';
同時需要更新Route的聲明語句
<Route path="/" component={Home} />
// 更新為
<Route path='/welcome' element={<Home/>} />
?
Redirect組件
一般寫在所有路由注冊的最下方, 當所有路由都無法匹配時,跳轉到Redirect指定的路由
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Redirect to="/home"></Redirect>
from屬性和to屬性
- Switch中是Route 從上到下匹配,如果有一個匹配,后面的就不會再繼續匹配了
- Redirect的from屬性是當地址與from匹配(可以用正則)時,才會重定向到to屬性指定的路徑
- Redirect的from屬性如果沒有,則默認是匹配所有的路徑
exact
完全匹配 from;相當于 Route.exact。
strict
嚴格匹配 from;相當于 Route.strict。
?
路由組件和一般組件
- 寫法不同
一般組件: <Demo/>
路由組件: <Route path=" /demo" component={Demo}/>
- 存放位置不同
一般組件:components
路由組件:pages
- 接收到的props不同
一般組件:寫組件標簽時傳遞了什么,就能收到什么
路由組件:接收到三個固定的屬性
路由的執行過程
- 點擊Link組件(a標簽)會修改瀏覽器地址欄中的url
- React路由監聽到地址欄url的變化。
- Reat路由內部遍歷所有 Route組件,使用路由規則(path)與 pathname進行匹配。
- 當路由規則(path)能夠匹配地址欄中的pathname時,就展示渲染該 Route組件的內容
編程式導航
- 編程式導航:通過JS代碼來實現頁面跳轉
- history是 React路由提供的,用于獲取瀏覽器歷史記錄的相關信息。借助props.history對象上的API進行跳轉。**只有路由組件的props上才有history對象,**普通組件的props上的history是undefined。
- push(path):跳轉到某個頁面,參數path表示要跳轉的路徑
- go(n):前進或后退到某個頁面,參數n表示前進或后退頁面數量(比如:-1表示后退到上一頁)
為什么是從props上拿到history對象呢?我們創建的組件是沒有history對象的,在Route組件中渲染了自己創建的組件,然后通過prop傳了history進去。組件就可以通過props拿到history
import React from 'react'
import ReactDom from 'react-dom'
// 導入路由組件
import {
BrowserRouter as Router, Route, Link } from "react-router-dom";
// import { HashRouter as Router, Route, Link } from "react-router-dom";
class App extends React.Component {
render() {
return (
<Router>
<div>
<p>編程式導航</p>
<Link to="/login">去登錄頁面</Link>
<Route path="/login" component={
Login}></Route>
<Route path="/home" component={
Home}></Route>
</div>
</Router>
)
}
}
class Login extends React.Component {
handleLogin = () => {
this.props.history.push("/home");
}
render() {
return (
<div>
<h1>登錄界面</h1>
<button onClick={
this.handleLogin}>首頁</button>
</div>
)
}
}
const Home = (props) => {
const goBack = () => {
props.history.go(-1);
}
return (
<div>
<h2>首頁</h2>
<button onClick={
goBack}>返回</button>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'));
?
withRouter
一般組件的props上的history是undefined,無法使用編程式導航的api。此時可以用withRouter函數。
- withRouter是一個函數,可以加工一般組件,讓一般組件具備路由組件所特有的API,通過props傳遞三個屬性:history/location/match
- withRouter的返回值是一個新組件
import React, {
Component } from 'react'
import {
withRouter } from 'react-router-dom'
// Header是一般組件
class Header extends Component {
back = () => {
this.props.history.goBack()
}
forward = () => {
this.props.history.goForward()
}
go = () => {
this.props.history.go(2)
}
render() {
console.log('Header組件中的props:', this.props)
return (
<div className="head">
<h1>React-router-dom</h1>
<button onClick={
this.back}>回退</button>
<button onClick={
this.forward}>前進</button>
<button onClick={
this.go}>go</button>
</div>
)
}
}
// withRouter是一個函數,可以加工一般組件,讓一般組件具備路由組件所特有的API
// withRouter的返回值是一個新組件
export default withRouter(Header)
?
默認路由
默認路由表示進入到頁面后就能匹配到的路由,并展示對應的組件
<Route path="/" component={
Login}></Route>
?
匹配模式
模糊匹配模式
- 默認情況下, React路由是模糊匹配模式
- 模糊匹配規則:只要pathname以path開頭就會匹配成功,對應的組件就會被渲染出來
- path代表Route組件的path屬性
- pathname代表Link組件的to屬性(也就是location.pathname)
精確匹配
- 給 Route組件添加exact屬性,讓其變為精確匹配模式
- 精確匹配:只有當path和 pathname完全匹配時才會展示該路由
嚴格匹配不要隨便開啟,需要再開,有些時候開啟會導致無法繼續匹配二級路由
class App extends React.Component {
render() {
return (
<Router>
<div>
<Link to="/">首頁</Link>
<Link to="/login">登錄頁面</Link>
{
/* 此時該組件只會匹配"/"這一種情況 */}
<Route path="/" exact component={
Home}></Route>
<Route path="/login" component={
Login}></Route>
</div>
</Router>
)
}
}
push和replace模式
默認情況下,路由的切換是push模式,點擊后退按鈕時還可以回到上一個路由。如果想要開啟replace模式,需要在Link組件上添加replace屬性
<Link to="/home" replace></Link>
?
嵌套路由
- 注冊子路由時要寫上父路由的path值
- 路由的匹配是按照注冊路由的順序進行的
?
向路由組件傳遞參數
params參數
import React, {
Component } from 'react'
import {
Link, Route } from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{
id: 1,
title: '消息1',
},
{
id: 2,
title: '消息2',
},
{
id: 3,
title: '消息3',
},
],
}
render() {
return (
<div>
<ul>
{
this.state.messageArr.map((msgObj) => {
return (
<li key={
msgObj.id}>
{
/* 1、向路由組件傳遞params參數 */}
<Link to={
`/home/message/detail/${
msgObj.id}/${
msgObj.title}`}>
{
msgObj.title}
</Link>
</li>
)
})}
</ul>
{
/* 2、聲明接受params參數 */}
<Route path="/home/message/detail/:id/:title" component={
Detail} />
</div>
)
}
}
接收params參數
import React, {
Component } from 'react'
const detailData = [
{
id: 1, content: '內容1' },
{
id: 2, content: '內容2' },
{
id: 3, content: '內容3' },
]
export default class Detail extends Component {
render() {
console.log('detail:', this.props)
// 接收params參數
const {
id, title } = this.props.match.params
let index = parseInt(id) - 1
return (
<ul>
<li>id:{
id}</li>
<li>title:{
title}</li>
<li>content:{
detailData[index].content}</li>
</ul>
)
}
}
?
search參數
import React, {
Component } from 'react'
import {
Link, Route } from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{
id: 1,
title: '消息1',
},
{
id: 2,
title: '消息2',
},
{
id: 3,
title: '消息3',
},
],
}
render() {
return (
<div>
<ul>
{
this.state.messageArr.map((msgObj) => {
return (
<li key={
msgObj.id}>
{
/* 1、向路由組件傳遞search參數 */}
<Link to={
`/home/message/detail?id=${
msgObj.id}&title=${
msgObj.title}`}>
{
msgObj.title}
</Link>
</li>
)
})}
</ul>
{
/* 2、search參數無須聲明接收,正常注冊路由即可 */}
<Route path="/home/message/detail" component={
Detail} />
</div>
)
}
}
接收參數
import React, {
Component } from 'react'
import qs from 'querystring'
let obj = {
id: 1, name: 'mingming' }
console.log(qs.stringify(obj)) // id=1&name=mingming 這是urlencoded格式
let query = 'id=1&name=mingming'
console.log(qs.parse(query)) // {id: "1", name: "mingming"}
const detailData = [
{
id: 1, content: '內容1' },
{
id: 2, content: '內容2' },
{
id: 3, content: '內容3' },
]
export default class Detail extends Component {
render() {
console.log('detail:', this.props)
// 3、接收search參數
const {
id, title } = qs.parse(this.props.location.search.slice(1))
let index = parseInt(id) - 1
return (
<ul>
<li>id:{
id}</li>
<li>title:{
title}</li>
<li>content:{
detailData[index].content}</li>
</ul>
)
}
}
state參數
import React, {
Component } from 'react'
import {
Link, Route } from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{
id: 1,
title: '消息1',
},
{
id: 2,
title: '消息2',
},
{
id: 3,
title: '消息3',
},
],
}
render() {
return (
<div>
<ul>
{
this.state.messageArr.map((msgObj) => {
return (
<li key={
msgObj.id}>
{
/* 1、向路由組件傳遞state參數 */}
<Link
to={
{
pathname: '/home/message/detail',
state: {
id: msgObj.id, title: msgObj.title },
}}>
{
msgObj.title}
</Link>
</li>
)
})}
</ul>
{
/* 2、state參數無須聲明接收,正常注冊路由即可 */}
<Route path="/home/message/detail" component={
Detail} />
</div>
)
}
}
接收參數
import React, {
Component } from 'react'
const detailData = [
{
id: 1, content: '內容1' },
{
id: 2, content: '內容2' },
{
id: 3, content: '內容3' },
]
export default class Detail extends Component {
render() {
console.log('detail:', this.props)
// 接收state參數
// 如果清空history對象或者清除瀏覽器的歷史記錄,此時刷新頁面會報錯state是undefined
const {
id, title } = this.props.location.state || {
}
let msgObj = detailData.find((item) => item.id === id) || {
}
return (
<ul>
<li>id:{
id}</li>
<li>title:{
title}</li>
<li>content:{
msgObj.content}</li>
</ul>
)
}
}
編程式導航傳遞參數
pushShow = (msgObj) => {
// 傳遞params參數
this.props.history.push(`/home/message/detail/${
msgObj.id}/${
msgObj.title}`)
// 傳遞search參數
this.props.history.push(`/home/message/detail?id=${
msgObj.id}&title=${
msgObj.title}`)
// 傳遞state參數
this.props.history.push(`/home/message/detail`, {
id: msgObj.id,
title: msgObj.title,
})
}
前端學習交流QQ群,群內學習討論的氛圍很好,大佬云集,期待你的加入:862748629,點我加入
總結
- 上一篇: Codeforce 322E Ciel
- 下一篇: 数据结构学习路线