React路由介绍(基于ReactRouter v6)
1.路由的种类
现代的前端应用大多都是 SPA(单页应用程序)single page application,也就是只有一个 HTML 页面的应用程序。因为它的用户体 验更好、对服务器的压力更小,所以更受欢迎。
为了有效的使用单个页面来管理原来多页面的功能,前端由此应运而生。
前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)
- 前端路由是一套映射规则,在React中,是 URL路径 与 组件 的对应关系 。一个路由就是一对映射关系–>key:value
- 使用React路由简单来说,就是配置 路径和组件(配对)
在react中路由分为两大种类,一种是HashRouter,而另外一种就是BrowserRouter
其中HashRouter的实现方式是使用url的哈希值实现
而BrowserRouter使用H5的history.pushState API实现
2.路由的基本用法
2.1 路由安装
在开始使用路由之前,我们需要给react安装路由插件
npm install react-router-dom@6
//新版
2.2路由映射
所有的路由都需要使得路由能映射到正确的地方,所以在此之前需要对路由的映射做一番讲解
在react-dom-Router中有几个组件可以用来规定路由的映射
Routes
在这一个元素包裹这下可以包裹多个路由组,其中路由组是由Route组成的,代码示例如下:
import{ Routes ,Route} from 'react-router-dom'
import Class from '../pages/Class/Class'
import ClassOnline from '../pages/ClassOnline/ClassOnline'
import Index from '../pages/Index/index'
function MainRouter () {
return (
<>
<Routes>
<Route path='/' element={<Index></Index>} />
<Route path='/Class' element={<Class></Class>} />
<Route path='/ClassOnline' element={<ClassOnline></ClassOnline>} />
</Routes>
</>
)
}
export default MainRouter
可以看到,在代码中利用Route中的path以及element的对应,建立起这一个映射关系,之后将建立起映射关系的Routes到到处去,给到Router进行使用,这样一个Router的基本逻辑就搭建完毕了
2.2.1 HashRouter的基本使用
Hash路由实际上就是利用url的不同生成了特定的Hash值,通过这样的方式找到相对于的路由组件,而这个的好处就是在于不需要管前面的路径有什么,他是以#后面来计算的,如下所示
http://localhost:5173/#/Class
要使用HashRouter就需要在需要路由的地方使用HashRouter包裹起来,如下所示
import './App.scss'
import MainRouter from '../../router/MainRoute'
import Footer from '../layout/Footer'
import Header from '../layout/Header'
function App() {
return (
<>
<div>
<Header></Header>
<div className='MainWindows'>
<MainRouter ></MainRouter>
</div>
<Footer></Footer>
</div>
</>
)
}
export default App
页面如下所示
点击其他的页面进行跳转
这张图的阐明了HashRouter的工作机制,HashRouter不管前面是什么,他根据**#**这一个进行截断,根据不同的URL得出不同的Hash值,将组件与这些Hash值相映射
上述的代码写的Routes其实只是告诉程序我有这些组件,需要跳转进这些组件,还需要靠我们改变路径url,实现跳转,我使用的方式之一是利用React的useNavigate进行url的改变,这个函数需要提供一个参数,就是需要跳转的url。,具体编写如下所示
import { useNavigate } from 'react-router-dom'
import './Header.scss'
function Header (){
const navigate = useNavigate()
return (
<>
<div className='header'>
<ul className='header-bar'>
<li className='header-bar-item' onClick={()=>{
navigate("/")
}} > 首页</li>
<li className='header-bar-item'
onClick={()=>{
navigate("/Class")
}}
>班级介绍</li>
<li className='header-bar-item' onClick={()=>{
navigate("/ClassOnline")
}} >班级在线</li>
</ul>
</div>
</>
)
}
export default Header
通过这样的方式就基本上形成了一个基础的路由。
2.2.2 BrowserRouter的基本使用
BrowserRouter的用法上其实和HashRouter大同小异,但是他们的实现原理是完全不同的。如果说HashRouter是使用Hash做了一个绝对的定位。那么BrowserRouter便是通过基于父路径的相对定位做到的URL整体映射关系。
在代码的写法是基本一样的,只需要将包含APP的HashRouter改成BrowserRouter即可,如下所示
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from '../App/App.tsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
)
页面的效果如图所示
由此可见,两者路由的使用方式其实大同小异,只是底层的实现逻辑是不一样的。BrwoserRouter的实现逻辑是利用histroy的API进行跳转实现。
3.高级用法
3.1嵌套路由
3.1.1 什么是嵌套路由
嵌套路由是指将一个路由配置嵌套在另一个路由配置中的情况。通过嵌套,我们可以在应用中创建更复杂的页面结构,使得页面的层次关系更加清晰。
首先抽象路由表,之后通过递归进行求解子路由,这里只显示两层路由,实际上可以使用 一个递归函数递归出路由来,如下所示
import{ Routes ,Route} from 'react-router-dom'
import Class from '../pages/Class/Class'
import ClassOnline from '../pages/ClassOnline/ClassOnline'
import Index from '../pages/Index/index'
function MainRouter () {
const router = [{
path: "/",
el: <Index></Index>,
children: []
},
{
path: "/",
el: <Index></Index>,
children: []
},
{
path: "/Class/:id/*",
el: <Class></Class>,
children: [{
path:"/Class/:id/Index",
el:<Index></Index>,
children: []
}]
},
{
path: "/ClassOnline",
el: <ClassOnline></ClassOnline>,
children: []
},
]
return (
<>
<Routes>
{
router.map((item,index) =>{
return (
<>
<Route key={index} path={item.path} element={item.el} ></Route>
{item.children.length > 0 ?
item.children.map((child,index1) =>{
return (
<Route key ={index1+100} path={child.path} element={child.el}></Route>
)
}):null}
</>
)
})
}
</Routes>
</>
)
}
export default MainRouter
注意:如果是嵌套url,那在父url中后面需要加入/*,表示还有子路由
3.2 路由传参
3.2.1固定传入参数
需求:改写我们的路由,传入相应的参数,之后在页面显示该参数,如下所示
使用useNavigate进行跳转
import { useNavigate } from 'react-router-dom'
import './Header.scss'
function Header (){
const navigate = useNavigate()
return (
<>
<div className='header'>
<ul className='header-bar'>
<li className='header-bar-item' onClick={()=>{
navigate("/")
}} > 首页</li>
<li className='header-bar-item'
onClick={()=>{
navigate("/Class?id=2344")
}}
>班级介绍</li>
<li className='header-bar-item' onClick={()=>{
navigate("/ClassOnline")
}} >班级在线</li>
</ul>
</div>
</>
)
}
export default Header
在跳转之后的页面,需要使用usesearchParam进行接受参数
import { useNavigate } from 'react-router-dom'
import './Header.scss'
function Header (){
const navigate = useNavigate()
return (
<>
<div className='header'>
<ul className='header-bar'>
<li className='header-bar-item' onClick={()=>{
navigate("/")
}} > 首页</li>
<li className='header-bar-item'
onClick={()=>{
navigate("/Class?id=2344")
}}
>班级介绍</li>
<li className='header-bar-item' onClick={()=>{
navigate("/ClassOnline")
}} >班级在线</li>
</ul>
</div>
</>
)
}
export default Header
由此可见。如果把固定的字符串改成变量字符串,似乎也是一个动态粗汉儒参数的办法,但是这样的url不符合restful风格并且管理起来比较麻烦,于是固定参数传入参数这一个方法一般只适用于几乎没有什么变动的静态页面。
一般来说,需要采取传入参数的办法
3.2.2 动态传入参数
而动态传入参数的话需要改动的地方与固定参数的地方几乎一样,具体如下
首先需要在router中修改路由,如下所示
<Route path='/Class/:id' element={<Class></Class>} />
之后在导航中的写法要改成restful风格的url
<li className='header-bar-item'
onClick={()=>{
navigate("/Class/2344")
}}
>班级介绍</li>
之后在接受参数的时候使用的叫做useParam这一个hook,具体如下
import { useParams, useSearchParams } from "react-router-dom"
function Class (){
const Params = useParams()
let id = Params.id
return (<>
<div>
这是calss页面
{id}
</div>
</>)
}
export default Class
之后就可以正常跳转了
那么如何传一个对象呢?通过序列化之后反序列化就可以了
4.注意事项
4.1 抽象路由组件
抽象路由其实就是将路由的信息通过另一个文件记录下来,之后导出去,再通过map逐一渲染即可,如下所示
const router = [{
path: "/",
el: <Index></Index>,
children: []
},
{
path: "/",
el: <Index></Index>,
children: []
},
{
path: "/Class/:id",
el: <Class></Class>,
children: [{
path:"/Class/:id/Index",
el:<Index></Index>,
Children: []
}]
},
{
path: "/ClassOnline",
el: <ClassOnline></ClassOnline>,
children: []
},
]
export default routers
之后在router的地方渲染
return (
<>
<Routes>
{
router.map((item,index) =>{
return (
<>
<Route key={index} path={item.path} element={item.el} ></Route>
</>
)
})
children: [{
path:“/Class/:id/Index”,
el:,
Children: []
}]
},
{
path: "/ClassOnline",
el: <ClassOnline></ClassOnline>,
children: []
},
]
export default routers
之后在router的地方渲染
```tsx
return (
<>
<Routes>
{
router.map((item,index) =>{
return (
<>
<Route key={index} path={item.path} element={item.el} ></Route>
</>
)
})
这样就可以将多个路由通过文件导入的方式进行统一管理)