最快速最直白带你上手 React-Router6
开源
个人开源的leno-admin
后台管理项目,前端技术栈:reactHooks
、ant-design
;后端技术栈:koa
、mysql
、redis
,整个项目包含web端
、electron客户端
、mob移动端
、template基础模板
,能够满足你快速开发一整套后台管理项目;如果你觉得不错,就为作者点个✨star
✨吧,你的支持就是对我最大的鼓励;
一、React Router 种类
- 1、
react-router
该包是核心库; - 2、
react-router-dom
该包是专门用来开发web网页端的路由包; - 3、
react-router-native
该报是专门用来开发移动端的,里面专门添加了ReactNative的API;
本文主要是讲解react-router-dom的使用;
二、安装
首先是进行npm包的安装,React-Router
只需要安装一个包即可使用所有的路由API,还是比较方便的;
npm i react-router-dom
复制代码
三、BrowserRouter与HashRouter
HashRouter
直观感受就是网页路径带 #;
BrowserRouter
直观感受就是网页路径不带 #,项目开发中主要使用BrowserRouter
;
在index.tsx
入口文件内从react-router-dom
中按需引入BrowserRouter
对入口组件进行包裹,代码如下:🥗
import React from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { ConfigProvider } from 'antd'
import App from '@/App'
const root = document.getElementById('root')
if (root) {
createRoot(root).render(
<BrowserRouter>
<ConfigProvider>
<App />
</ConfigProvider>
</BrowserRouter>,
)
}
复制代码
四、路由链接与注册路由
react-router
里面的路由链接分为声明式导航NavLink,Link
与编程式导航useNavigate
,注册路由则为Routes
、Route
、Outlet
;
4-1 声明式导航(NavLink、Link) (即路由跳转功能)
NavLink
是带上有选中样式功能配置的
// App.tsx
<NavLink className={currentHighLightFn} to="/home">
GoHome
</NavLink>
<NavLink className={currentHighLightFn} end to="/about/news">
go about
</NavLink>
复制代码
to
为跳转路径,后面跟路径地址,可以写/home
也可以直接写home
;当然如果是多级路由可写/about/news
,如果是嵌套路由的话,子路可以直接写news
;
end
属性,如果Navink
添加了end属性后,若Home的子组件匹配成功,那Home本身的高亮效果就会没有,也就是说,没添加end
的话,则父组件和子组件都有高亮效果,添加end
的话,则子组件有高亮效果的话,父组件身上的就会消失;
Link
<Link to={`detail/${item.id}/${item.content}`}>{item.content}</Link>
// 用法基本与NavLink相似,但是没有高亮效果;
复制代码
4-1-1 选中变色
其实选中变色就是react-router
提供一个变量isActive
,如果isActive
为true,就表示选中当前路由按钮,那就给他的className
添加变色的类名即可,直接上封装的函数currentHighLightFn
:
// utils/index.ts
import { IIsActive } from '@/type'
/* 当前高亮函数 */
export function currentHighLightFn(obj: IIsActive): string {
const { isActive } = obj
return isActive ? 'sidebar current-highlight' : 'sidebar'
}
//type/index.d.ts
export type IIsActive = {
isActive: boolean
}
复制代码
4-2 编程式导航 useNavigate
一般大多数时候声明式导航都无法满足我们的功能需求,其实在项目开发中我们更多使用的都是编程式导航,一般都是对方是否满足我们设置的条件才允许对方跳转,否则不允许对方跳转,直接上代码:
// views/login/index.tsx
import { useNavigate } from 'react-router-dom'
export default function Login() {
let navigate = useNavigate() // 调用useNavigate函数,返回函数,一般命名navigate
const onFinish = async ({ username, password }: ILogin) => {
const { status, token } = (await loginAPI(username, password)) as any
if (status === 200) {
localStorage.setItem('token_01', token)
navigate('/home')
message.success('登录成功!')
}
}
// 下面简写了,这是一个登录功能,但是全部代码太长了,所以下面就简单的布置一个按钮
return (
<div>
<button onClikc='onFinish'>点击登录</button>
</div>
)
}
复制代码
navigate(第一个参数为跳转路径[如果是子路由则不要 \ ],第二个为一个对象{
replace:false(默认为false,表示是否替换路径,覆盖from的路径),
state:{
id:11
}(传输数据,state属性名不可以变)
})
复制代码
额外补充点:如果navigate
传-1或1就是前进一层路由或者后退一层路由;
4-3 注册路由基础版(Routes、Route)
Route
必须需要被Routes
包裹,如果直接单写Route
则会报错无法运行,Route
有两个关键的属性,一个是path
路径,一个则是element
绑定的组件,直接上代码案例:
{/* 注册路由 */}
<Routes>
<Route path="/about" element={<About />} />
<Route caseSensitive path="/home" element={<Home />} />
<Route path="/" element={<Navigate to="/about" />} /> // 重定向
</Routes>
复制代码
Navigate
路径重定向,后面需要跟to=路径
,一般设置根路径/
的跳转页面地址;
Route
中的 caseSensitive
属性:匹配的时候是否区分大小写(默认值为false)
4-4 注册路由表useRoutes,工程化使用React-router
虽然Routes
与Route
使用起来非常方便简单,但是在项目开发中,经常使用的都是路由表useRoutes
,也即是模块化路由管理,对未来项目路由的管理及扩展都是非常友好的,直接上代码:
// App.tsx入口文件
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import '@/style/App.scss'
/* 引入路由表 */
import routes from '@/routes'
/* 引入工具函数 */
import { currentHighLightFn } from '@/utils'
import './style/App.css'
export default function App() {
/* 路由表 */
const element = useRoutes(routes)
return (
<div>
{/* 路由连接 */}
<NavLink className={currentHighLightFn} to="home">
go home
</NavLink>
<NavLink className={currentHighLightFn} end to="/about">
go about
</NavLink>
-----------------------------
{/* 注册路由 */}
{element}
</div>
)
}
复制代码
路由文件目录格式:
分路由模块about.tsx
代码格式:
import React from 'react'
/* 路由页面 */
import About from '@/views/About'
import News from '@/views/News'
import Message from '@/views/Message/indx'
import Detail from '@/views/Detail'
export default {
path: '/about',
element: <About />,
children: [
{
path: 'news',
element: <News />,
},
{
path: 'message',
element: <Message />,
children: [
{
path: 'detail/:id/:content',
element: <Detail />,
},
],
},
],
}
复制代码
路由入口文件index.tsx
代码如下:
import React from 'react'
import { Navigate } from 'react-router-dom'
/* 路由页面 */
import Home from '@/views/Home'
/* 子路由 */
import aboutRoute from './modules/about'
const commentRoutes = [aboutRoute]
export default [
{
path: '/home',
element: <Home />,
},
...commentRoutes,
{
path: '/',
element: <Navigate to="/home" />,
},
]
复制代码
4-5 指定路由呈现位置Outlet
对于子路由呈现位置,需要使用Outlet
在指定位置进行呈现展位,代码如下:
import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'
/* 工具函数 */
import { currentHighLightFn } from '@/utils'
/* ant */
import { Button } from 'antd'
export default function About() {
return (
<div>
<h1>About</h1>
{/* 路由连接 */}
<NavLink className={currentHighLightFn} to="news">
News
</NavLink>
<NavLink className={currentHighLightFn} to="message">
Message
</NavLink>
<Button type="primary" value="large">
Large
</Button>
{/* 注册路由 */}
<Outlet />
</div>
)
}
复制代码
五、路由传值
5-1 路由params参数的使用
第一步 在父组件Message
声明数据
import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'
export default function Message() {
const [message] = useState([
{ id: 0, content: '我是第一个' },
{ id: 1, content: '我是第二个' },
{ id: 2, content: '我是第三个' },
])
return (
<div>
<h2>Message 我是二级路由</h2>
{message.map((item) => {
return (
<li key={item.id}>
<Link to={`detail/${item.id}/${item.content}`}>{item.content}</Link>
// 数据通过路径传到子路由,需按照上面的格式传值;
</li>
)
})}
--------------- 三级路由 ---------------------
<Outlet />
</div>
)
}
复制代码
第二步 路由表内值路径占位
import React from 'react'
/* 路由页面 */
import About from '@/views/About'
import News from '@/views/News'
import Message from '@/views/Message/indx'
import Detail from '@/views/Detail'
export default {
path: '/about',
element: <About />,
children: [
{
path: 'news',
element: <News />,
},
{
path: 'message',
element: <Message />,
children: [
{
path: 'detail/:id/:content', // 此处对要传输的数据进行占位
element: <Detail />,
},
],
},
],
}
复制代码
第三步 子组件内通过useParams
进行取值
import React from 'react'
import { useParams } from 'react-router-dom'
export default function Detail() {
const { id, content } = useParams()
return (
<div>
<h3>{id}</h3>
<h3>{content}</h3>
</div>
)
}
复制代码
通过简单的三步,便可以完成params传值,还不快去试试呀!
5-2 路由的search参数(类似于Vue的query参数)
第一步 在父组件Message
声明数据
{message.map((item) => {
return (
<li key={item.id}>
<Link to={`detail?id=${item.id}&content=${item.content}`}>{item.content}</Link>
// 数据通过路径传到子路由,需按照上面的格式传值;
</li>
)
})}
复制代码
第二步 路由表内不用占位
{
path: 'message',
element: <Message />,
children: [
{
path: 'detail',
element: <Detail />,
},
],
},
复制代码
第三步 子组件内通过useSearchParams
取值
import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function Detail() {
const [search,setSearch] = useSearchParams()
const id = search.get('id')
const content = search.get('content')
return (
<div>
<h3>{id}</h3>
<h3>{content}</h3>
</div>
)
}
复制代码
额外补充点,setSearch
对当前子组件的search
参数进行更改
// 你可以写一个按钮,点击改变该子组件内部的search参数
<button onClick={()=>setSearch('id=111&content=我改变啦')}>点我更新页面search参数</button>
复制代码
额外补充点,useLocation
获取路由参数信息
import {useLocation} from 'react-router-dom'
const data = useLocation()
复制代码
获取的路由信息图片:
5-3 路由的state参数
第一步 在父组件Message
声明数据
{message.map((item) => {
return (
<li key={item.id}>
<Link to="detail" state={{
id:item.id,
content:item.content
}}>{item.content}</Link>
// 数据通过路径传到子路由,需按照上面的格式传值;
</li>
)
})}
复制代码
第二步 路由表内不用占位
{
path: 'message',
element: <Message />,
children: [
{
path: 'detail',
element: <Detail />,
},
],
},
复制代码
第三步 子组件内通过useLocation
取值
import React from 'react'
import { useLocation } from 'react-router-dom'
export default function Detail() {
const {state:{id,content} = useLocation()
return (
<div>
<h3>{id}</h3>
<h3>{content}</h3>
</div>
)
}
复制代码
六、额外知识点
6-1 useInRouterContext
判断你现在的组件是否处于被BrowserRouter
或HashRouter
路由包裹,如果被包裹了调用useInRouterContext()
会返回布尔值true
,没有被包裹住的话,返回的就是false
;
6-2 useNavigationType
useNavigationType
的作用是得知用户是如何进来当前页面的;
返回值:POP
,PUSH
,REPLACE
; POP
: 重新刷新当前页面打开了这个路由组件;
6-3 useOutlet
useOutlet
检测嵌套路由是否挂载,如果已经挂载了,返回值回事嵌套的路由对象,如果没有挂载,则返回值为null
;
6-4 useResolvedPath
帮你解析路由路径:
console.log(useResolvedPath('/home/?id=111&name=zzz'))
=> 浏览器打印:{pathname:'/home',search:'?id=111&name=zzz'}