Skip to content

路由搭建

更新: 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>
}

道友再会.