0%

react中的路由设置

中间件的实现

React-Router路由

URL的hash:

  • URL的hash也就是锚点(#), 本质上是改变window.location的href属性
  • 我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新

React Router的安装

1
npm install react-router-dom

基本使用

BrowserRouter或HashRouter:

  • Router中包含了对路径改变的监听,并且会将相应的路径传递给子组件
  • BrowserRouter使用history模式
  • HashRouter使用hash模式

路由的配置与使用

要使用的组件前面添加路由设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { HashRouter } from 'react-router-dom';


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
// 可以选择HashRouter/BrowserRouter
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>
);

路由的映射

映射设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { PureComponent } from 'react'
import { Routes, Route } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
export default class App extends PureComponent {
render() {
return (
<div>
<h2>Header</h2>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/my" element={<MyMusic/>}/>
</Routes>
</div>
</div>
)
}
}

注:

  • 如果使用HashRouter,浏览器的路径设置一定要加/#/,否则是失效的

image-20230402152504555

image-20230402152522490

路由的配置跳转

Link元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { PureComponent } from 'react'
import { Routes, Route, Link } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
export default class App extends PureComponent {
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<Link to="/">发现音乐</Link>
<br />
<Link to="/my">我的音乐</Link>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/my" element={<MyMusic/>}/>
</Routes>
</div>
</div>
)
}
}

image-20230402153039156

注:

  • Link元素会自动渲染为a元素
  • 如果要更改这个a元素的样式,需要使用NavLink组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
import "./index.css"
export default class App extends PureComponent {
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/">发现音乐</NavLink>
<br />
<NavLink to="/my">我的音乐</NavLink>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/my" element={<MyMusic/>}/>
</Routes>
</div>
</div>
)
}
}

index.css代码:

1
2
3
.active{
color: red;
}

image-20230402153943491

注:

  • NavLink元素会自动给当前渲染的a元素添加active类,所以可以通过active去设置当前渲染的样式
  • 而如果active类在其他地方已经使用过了呢,使用active就容易引起冲突,react-router提供了className属性去自定义我们的类名称,属性应该传入函数
  • 同样react-router为我们提供了style属性去定义样式,属性应该传入函数

使用style属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
export default class App extends PureComponent {
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
{/* 这里给style传入的函数,返回的一个对象,而且会自动传入一个包含isActive的参数,可以通过解构出isActive用于逻辑判断 */}
<NavLink to="/" style={({isActive})=>{ return isActive?{color:"red",fontSize:24}:{}}}>发现音乐</NavLink>
<br />
<NavLink to="/my" style={({isActive})=>{ return isActive?{color:"red",fontSize:24}:{}}}>我的音乐</NavLink>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/my" element={<MyMusic/>}/>
</Routes>
</div>
</div>
)
}
}

image-20230402154824768

使用className属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
import "./index.css"
export default class App extends PureComponent {
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
<br />
<NavLink to="/my" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/my" element={<MyMusic/>}/>
</Routes>
</div>
</div>
)
}
}

image-20230402155112040

  • 如当页面location为/时,自动跳转到/home/页面
  • 这种情况与上面直接element的直接区别为:这种情况下不会出现”/“的情况,因为会直接跳转,而上面是替换匹配到的组件
  • 导航的所有东西都是放在#后面的,#前面在解析时会忽略

image-20230402160154144

image-20230402160916685

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink, Navigate } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
import "./index.css"
export default class App extends PureComponent {
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
<br />
<NavLink to="/my" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path='/' element={<Navigate to="/home"/>}/>
<Route path="/home" element={<Home/>}/>
<Route path="/my" element={<MyMusic/>}/>
</Routes>
</div>
</div>
)
}
}
Not Found页面配置
  • 当所有的Route都未匹配成功时,当前组件内容为空,而有时想给用户提醒输入了错误的url,则需要一个可以匹配所有字符的默认Route

image-20230402160512717

  • react-route中的通配符为’*‘,因此将一个path=’*‘放在最后,那么如果前面的path都没匹配上,最后一个肯定可以匹配,并找到默认页面

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      import React, { PureComponent } from 'react'
      import { Routes, Route, NavLink, Navigate } from 'react-router-dom'
      import Home from './components/Home'
      import MyMusic from './components/MyMusic'
      import NotFound from './components/NotFound'
      import "./index.css"
      export default class App extends PureComponent {
      render() {
      return (
      <div>
      {/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
      <div className='header'>
      <NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
      <br />
      <NavLink to="/my" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
      </div>
      <div className="content">
      {/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
      <Routes>
      <Route path='/' element={<Navigate to="/home"/>}/>
      <Route path="/home" element={<Home/>}/>
      <Route path="/my" element={<MyMusic/>}/>
      <Route path='*' element={<NotFound/>}/>
      </Routes>
      </div>
      </div>
      )
      }
      }

路由的嵌套

在实际开发中,经常出现路由之间存在嵌套关系——如网易云中的路由

image-20230402161440408

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink, Navigate } from 'react-router-dom'
import Home from './components/Home'
import MyMusic from './components/MyMusic'
import NotFound from './components/NotFound'
import Recommend from './components/Recommend'
import TopList from './components/TopList'
import "./index.css"
export default class App extends PureComponent {
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
<NavLink to="/my" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path='/' element={<Navigate to="/home"/>}/>
<Route path="/home" element={<Home/>}>
<Route path='/home' element={<Navigate to="/home/discover"/>}/>
<Route path='/home/discover' element={<Recommend/>}/>
<Route path='/home/toplist' element={<TopList/>}/>
</Route>
<Route path="/my" element={<MyMusic/>}/>
<Route path='*' element={<NotFound/>}/>
</Routes>
</div>
</div>
)
}
}

注:

  • 通过上述写法可以实现嵌套,在Home组件中编写下一层的路由跳转逻辑

    • Home.jsx的代码——Outlet是NavLink元素得到组件的位置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import React, { PureComponent } from 'react'
    import { NavLink, Outlet } from 'react-router-dom'
    import "../index.css"
    export default class Home extends PureComponent {
    render() {
    return (
    <div className='home-container'>
    <div>home</div>
    <div className='home-content'>
    <NavLink to="/home/discover">推荐</NavLink>
    <NavLink to="/home/toplist">排行榜</NavLink>
    </div>
    {/* <Outlet/>是NavLink元素得到组件的位置 */}
    <Outlet/>
    </div>
    )
    }
    }

    image-20230402164901837

  • 第二层路由最终会变成第一层路由的子组件,而如果我们在两个’/‘路由的时候,想让他在第一层,而不出现在第二层呢——可以在第一层时,不写element

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <Routes>
    <Route path='/' element={<Navigate to="/home"/>}/>
    <Route path="/home">
    <Route path='/home' element={<Navigate to="/home/discover"/>}/>
    <Route path='/home/discover' element={<Recommend/>}/>
    <Route path='/home/toplist' element={<TopList/>}/>
    </Route>
    <Route path="/my" element={<MyMusic/>}/>
    <Route path='*' element={<NotFound/>}/>
    </Routes>

    image-20230402165329191

手动实现路由的跳转

  • 上面的跳转实现主要靠NavLink自动生成的a元素,但有时候我们想要使用其他组件跳转,或者在逻辑过程中跳转,这种方法就不太好实现
  • hooks提供了useNavigate函数,返回一个navigate对象,可以通过这个对象在代码中进行跳转
  • 但是hooks只能在函数组件中使用,类组件不能直接使用useNavigate
    • 对于对状态要求不高的类组件,可以更改为函数组件使用useNavigate
    • 对于有很多状态的类组件,可以使用高阶组件,在其外包裹一个函数组件,通过参数的形式,将navigate对象传递给类组件使用

高阶函数代码:

1
2
3
4
5
6
7
8
9
10
11
12
import { useNavigate } from "react-router-dom"

function hooksFunc(Wrapper){
return (props)=>{
const navigate = useNavigate()
const router = {
navigate
}
return <Wrapper {...props} router = {router}/>
}
}
export default hooksFunc

要被包裹的组件代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink, Navigate } from 'react-router-dom'
import Home from './components/Home'
import hooksFunc from './components/HooksFunction'
import MyMusic from './components/MyMusic'
import NotFound from './components/NotFound'
import Recommend from './components/Recommend'
import TopList from './components/TopList'
import Friend from './components/Frined'
import "./index.css"
class App extends PureComponent {
navigateFriend(){
const {navigate} = this.props.router
navigate("/friend")
}
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
<NavLink to="/my" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
<button onClick={()=>{this.navigateFriend()}}>关注</button>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path='/' element={<Navigate to="/home"/>}/>
<Route path="/home" element={<Home/>}>
<Route path='/home' element={<Navigate to="/home/discover"/>}/>
<Route path='/home/discover' element={<Recommend/>}/>
<Route path='/home/toplist' element={<TopList/>}/>
</Route>
<Route path="/my" element={<MyMusic/>}/>
<Route path='/friend' element={<Friend/>}/>
<Route path='*' element={<NotFound/>}/>
</Routes>
</div>
</div>
)
}
}
// 这里可以选择导出时用高阶函数处理,也可以选择先导出,在使用前处理
export default hooksFunc(App)

路由参数的传递

  • 动态路由的方式
  • search传递参数

动态路由

  • 比如/detail的path对应一个组件Detail
  • 如果我们将path在Route匹配时写成/detail/:id,那么 /detail/abc、/detail/123都可以匹配到该Route,并且进行显示
  • 这个匹配规则,我们就称之为动态路由
  • 通常情况下,使用动态路由可以为路由传递参数

App.jsx代码——配置Route:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink, Navigate } from 'react-router-dom'
import Home from './components/Home'
import hooksFunc from './components/HooksFunction'
import MyMusic from './components/MyMusic'
import NotFound from './components/NotFound'
import Recommend from './components/Recommend'
import TopList from './components/TopList'
import Friend from './components/Frined'
import "./index.css"
class App extends PureComponent {
navigateFriend(){
const {navigate} = this.props.router
navigate("/friend")
}
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
<NavLink to="/my/123/oww" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
<button onClick={()=>{this.navigateFriend()}}>关注</button>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path='/' element={<Navigate to="/home"/>}/>
<Route path="/home" element={<Home/>}>
<Route path='/home' element={<Navigate to="/home/discover"/>}/>
<Route path='/home/discover' element={<Recommend/>}/>
<Route path='/home/toplist' element={<TopList/>}/>
</Route>
{/* Route配置/:id/:name标志这里的id为参数 */}
<Route path="/my/:id/:name" element={<MyMusic/>}/>
<Route path='/friend' element={<Friend/>}/>
<Route path='*' element={<NotFound/>}/>
</Routes>
</div>
</div>
)
}
}
//
export default hooksFunc(App)

hooksFunction代码:——获取参数使用useParams的hook,依然不能直接在类组件中使用,因此与navigate一样,使用高阶组件包含

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { useNavigate, useParams } from "react-router-dom"

function hooksFunc(Wrapper){
return (props)=>{
const navigate = useNavigate()
const params = useParams()
const router = {
navigate,
params
}
return <Wrapper {...props} router = {router}/>
}
}
export default hooksFunc

MyMusic.jsx代码:——获取参数的组件

1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { PureComponent } from 'react'
import hooksFunc from './HooksFunction'

class MyMusic extends PureComponent {
render() {
const {params} = this.props.router
console.log(params)
return (
<div>MyMusic-{params.id}-{params.name}</div>
)
}
}
export default hooksFunc(MyMusic)

image-20230403175400198

search传递参数

search传递参数直接在跳转路由时设置(Link/navigate)——如navigate(“/friend?name=oww&age=22”)

App.jsx代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React, { PureComponent } from 'react'
import { Routes, Route, NavLink, Navigate } from 'react-router-dom'
import Home from './components/Home'
import hooksFunc from './components/HooksFunction'
import MyMusic from './components/MyMusic'
import NotFound from './components/NotFound'
import Recommend from './components/Recommend'
import TopList from './components/TopList'
import Friend from './components/Frined'
import "./index.css"
class App extends PureComponent {
navigateFriend(){
const {navigate} = this.props.router
// search参数传递不需要在Route中配置,而直接在Link或navigate中配置
navigate("/friend?name=oww&age=22")
}
render() {
return (
<div>
{/* 主要是通过路由的配置来更改当前页面的location,从而使下面的Routes匹配结果改变 */}
<div className='header'>
<NavLink to="/" className={({isActive})=>{ return isActive?"nav-active one":"one"}}>发现音乐</NavLink>
<NavLink to="/my/123/oww" className={({isActive})=>{ return isActive?"nav-active two":"two"}}>我的音乐</NavLink>
<button onClick={()=>{this.navigateFriend()}}>关注</button>
</div>
<div className="content">
{/* 这里会con上往下匹配,直至匹配到一个结果,后续就不匹配了*/}
<Routes>
<Route path='/' element={<Navigate to="/home"/>}/>
<Route path="/home" element={<Home/>}>
<Route path='/home' element={<Navigate to="/home/discover"/>}/>
<Route path='/home/discover' element={<Recommend/>}/>
<Route path='/home/toplist' element={<TopList/>}/>
</Route>
{/* Route配置/:id/:name标志这里的id为参数 */}
<Route path="/my/:id/:name" element={<MyMusic/>}/>
<Route path='/friend' element={<Friend/>}/>
<Route path='*' element={<NotFound/>}/>
</Routes>
</div>
</div>
)
}
}
//
export default hooksFunc(App)

hooksFunction代码:要获取传递的参数同样需要hooks,则同样需要用高阶函数包裹

  • useLocation:会获取到当前页面url的一些信息

  • useSearchParams:获取到当前页面的参数相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useLocation, useNavigate, useParams, useSearchParams } from "react-router-dom"

function hooksFunc(Wrapper){
return (props)=>{
const navigate = useNavigate()
const params = useParams()
const location = useLocation()
const [searchParams] = useSearchParams()
const query = Object.fromEntries(searchParams)
console.log("location", location)
console.log("searchParams", query)
const router = {
navigate,
params,
query
}
return <Wrapper {...props} router = {router}/>
}
}
export default hooksFunc

image-20230403181604295

路由的配置文件

  • 之前直接用Route组件定义路由,如果项目中路由很多的情况下,页面会相当混乱
  • 可以通过useRoutes的hook配置到一个地方集中管理
    • 早期,Route没有相关的api,需要借助于react-router-config
    • Router6.x中,为我们提供了useRoutes API完成相关的配置

image-20230403184434151