路由搭建
更新: 2025/3/13 12:08:48 字数: 0 字
需求分析
对于一个 SPA
项目来说,路由是一个很重要的模块,它负责将 URL
与 页面
进行映射,从而实现页面的切换。
在 raect
中,路由的实现是通过 react-router
插件实现的,它提供了一套完整的路由功能,包括路由的配置、路由的切换、路由的监听等。
路由的配置
在 react
中配置路由有两种方式,一种是通过 <BrowserRouter>
组件来在 JSX
中配置路由, 另一种是通过 <RouterProvider>
组件在 JS
中配置路由。我们要实现的路由是动态路由,及用户登录之后,根据用户的的当前的权限状态, 通过请求查询出其对应的路由信息,来动态的渲染路由。所以我们需要使用 <RouterProvider>
来配置路由。
使用 <BrowserRouter>
组件
<BrowserRouter>
组件是一个高阶组件,它接收一个 children
属性,用于指定路由的子组件。
路由数据
将路由进行分类,公共路由,任何人都有访问的权限,登录之后,根据用户的权限状态,动态的渲染路由。 另一种就是动态路由,即 path 为动态路由,即 /blog/articles/:artId
,其中 :artId
为动态参数。
tsx
export type RouterItem = {
name?: string, // 路由名称 --- 英文
path: string, // 路由路径
hidden?: boolean, // 是否在菜单中隐藏
element?: any,
component?: any,
redirect?: string, // 路由重定向
children?: RouterItem[],
meta?: {
title?: string, // 用于标签名称
icon?: any,
noCache?: boolean,
link?: null
keepAlive?: boolean
activeMenu?: string
}
}
// 公共路由
export const constantRoutes: RouterItem[] = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: lazy(() => import('@/pages/redirect/index'))
}
]
},
{
path: '',
component: Layout,
children: [
{
path: '/',
element: <Navigate to={"/index"}/>
},
{
path: '/index',
component: lazy(() => import('@/pages/home/index')),
name: 'Index',
meta: {title: '首页', icon: 'dashboard'}
},
{
path: '/system/personalCenter',
name: 'PersonalCenter',
hidden: true,
component: lazy(() => import('@/pages/system/personalCenter')),
meta: {title: '个人中心'}
},
{
path: '/system/role/assigningPermissions',
name: 'AssigningPermissions',
hidden: true,
component: lazy(() => import('@/pages/system/role/assigningPermissions')),
meta: {title: '分配权限', activeMenu: '/system/role'}
},
{
path: '/system/role-auth/user/:roleId',
name: 'EditMenu',
hidden: true,
component: lazy(() => import('@/pages/system/role/selectUser.tsx')),
meta: {title: '分配用户', activeMenu: '/system/role'}
}
]
},
{
path: '/text',
component: lazy(() => import('@/pages/text/upload/index'))
},
{
path: "/login",
hidden: true,
component: lazy(() => import("@/pages/login")),
},
{
path: "/qrCodeLogin",
hidden: true,
component: lazy(() => import("@/pages/login/qrCode")),
},
]
// 动态路由
export const dynamicRouter: RouterItem[] = [
{
path: '',
component: Layout,
children: [
{
path: '/blog/articles/edit/:artId',
name: 'BlogArticleEdit',
component: lazy(() => import('@/pages/blog/articles/editArticle.tsx')),
meta: {title: '编辑文章'}
},
{
path: '/blog/articles/picture/:artId',
name: 'BlogArticlePicture',
component: lazy(() => import('@/pages/blog/articles/pictureArticle.tsx')),
},
]
},
]
封装路由守卫组件
tsx
// main.tsx
import React from 'react';
import {createRoot} from 'react-dom/client';
import {BrowserRouter} from "react-router-dom";
import App from "@/App.tsx";
import config from "@/utils/config.ts";
const root = document.getElementById('root');
if (root) {
createRoot(root).render(
<BrowserRouter basename={config.baseUrl}>
<App/>
</BrowserRouter>
)
}
App.tsx
文件中,我们使用了 AuthRouter
组件,该组件用于实现路由的切换,以及根据当前用户的权限状态,动态的渲染路由。
tsx
// App.tsx
import React, {Suspense, useEffect} from 'react'
import AuthRouter from "@/components/RouterUtil/AuthRouter.tsx";
function App() {
return <>
<AuthRouter></AuthRouter>
</>
}
export default App
具有类似于路由守卫的效果,当用户访问一个没有权限的页面时,会自动跳转到登录页。
tsx
// AuthRouter.tsx
import {Navigate, Outlet, useLocation, useNavigate, useRoutes} from "react-router-dom";
import {isEmpty} from "@/utils/common.ts";
import {getRoutersUrl, getUserInfoUrl} from "@/api/system/user.ts";
import {setUserInfo} from "@/store/modules/userSlice.ts";
import {constantRoutes, createMenus, createRouters, createTagViews, dynamicRouter} from "@/router";
import {setDefinedRouter, setRouters} from "@/store/modules/routerSlice.ts";
import React, {Fragment, Suspense, useEffect, useState} from "react";
import {useDispatch, useSelector} from "react-redux";
import {globalMessageApi} from "@/components/OpComponent/Message";
/*
* 路由守卫组件
* */
export default function AuthRouter({children}: any) {
const whiteList = ['/login', '/qrCodeLogin', '/text']
const location = useLocation()
const {token, userInfo} = useSelector((state: any) => state.user)
const dispatch = useDispatch()
const navigator = useNavigate()
const localRouter = [...constantRoutes, ...dynamicRouter]
// 需要注入的路由表信息
const [routerList, setRouterList] = useState<any>([...createRouters(localRouter)])
const elements = useRoutes(routerList)
function checkLogin() {
const isLogin = !!token
if (!isLogin) {
// 未登录 在白名单中,放行
if (whiteList.includes(location.pathname)) return;
globalMessageApi.warning("未登录, 请先登录!")
// 跳转到登录页
navigator("/login")
} else {
// 已经登录了, 获取自己的信息,获取路由表信息
console.log("已经登录了", userInfo)
if (isEmpty(userInfo)) {
// 获取当前登录用户信息,再获取路由表信息
console.log("获取当前登录用户信息,再获取路由表信息")
getUserInfoUrl().then((res: any) => {
dispatch(setUserInfo(res.user))
getRoutersUrl().then((res: any) => {
const _routerList = createRouters([...localRouter, ...res.data])
dispatch(setDefinedRouter(res.data))
dispatch(setRouters(createTagViews([...localRouter, ...res.data])))
setRouterList([..._routerList])
console.log("最后生成的路由 routerL ist", _routerList)
})
}).catch((err: any) => {
console.log("获取路由信息失败", err)
if (location.pathname !== "/login") {
navigator("/login")
}
})
}
}
}
// 当路由发生变化时,检查登录状态
useEffect(() => {
checkLogin()
}, [location]);
return <Fragment>
{elements}
</Fragment>
}