中间件的实现
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,浏览器的路径设置一定要加/#/,否则是失效的


路由的配置跳转
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> ) } }
|

注:
- Link元素会自动渲染为a元素
- 如果要更改这个a元素的样式,需要使用NavLink组件
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代码:

注:
- 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> ) } }
|

使用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> ) } }
|

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


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

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

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> ) } }
|
注:
手动实现路由的跳转
- 上面的跳转实现主要靠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)
|
路由参数的传递
动态路由
- 比如/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)
|

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 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,则同样需要用高阶函数包裹
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
|

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