react-router-dom6路由守卫

公西宏毅
2023-12-01

1:router.ts,加载页面,设置routerBefore拦截函数

import PublicRouters from "./public-routers";
import HomeRouters from "./home-routers";
import {getLocalStorage} from "../utils";
import {onRouteBeforeRule, RouteObjectRule} from "../react-router-dom6-guard";

const routes: RouteObjectRule[] = [
    {

        path: '*',
        redirect: '/home',
    },
    {
        path: 'login',
        meta: {
            title: '登录',
        },
        page: () => import("../pages/login"),
    },
    {
        path: 'register',
        meta: {
            title: '注册',
        },
        page: () => import("../pages/register"),
    },
    {
        path: 'home',
        page: () => import("../pages/Home/home"),
        meta: {
            auth: true
        },
        children: [
            {
                index: true, //设置默认
                path: '*',
                meta: {
                    title: '个人主页',
                },
                page: () => import('../pages/Home/index/index')
            },
            {
                path: 'organization',
                meta: {
                    title: '组织机构管理',
                },
                page: () => import('../pages/Home/organization/index')
            },
        ],
    },
];

//根据路径获取路由
const checkAuth = (routers: Array<RouteObjectRule>, path: string) => {
    for (const data of routers) {
        if (data.path === path) return data;
        if (data.children) {
            const res = checkAuth(data.children, path);
            if (res) return res;
        }
    }
    return null
};

// 如果单独的页面需要设置权限等, 在outlet前根据路径,获取路由的详情,设置权限
const checkRouterAuth = (path: string) => checkAuth(routes, path);

//全局路由守卫
const onRouteBefore: onRouteBeforeRule = (meta, to) => {
    const {auth, title} = meta;
    if (title) {    // 动态修改页面title
        document.title = title || '统一中心管理';
    }
    console.log('to' , to)
    return to;
    // token权限验证
    return (auth && !getLocalStorage('access_token')) ? '/login' : to;
};

export default routes;

export {
    onRouteBefore,
    checkRouterAuth,
}


2:react-router-dom6-guard.tsx ,reactRouterdom6的路由统一封装,用到了useRouters

import {useRoutes, RouteObject, Navigate, useLocation} from 'react-router-dom';
import React, {Suspense} from "react";

interface FunctionRule {
    (): any
}

//meta规则
interface MetaRule {
    auth ?: boolean, //是否需要登录验证
    title ?: string, //页面title
    [name:string] : string | boolean, //其他参数
}

//单个路由规则
interface RouteObjectRule extends RouteObject {
    children ?: RouteObjectRule[], //子路由
    page ?: FunctionRule, //route导入页面的对象
    path ?: string, //页面路径
    redirect ?: string, //重定向地址 ,常用于设置页面默认地址
    meta ?: MetaRule, //页面参数
}

interface onRouteBeforeRule<meta = MetaRule , to = string> {
    (meta : meta, to : to) : any | never
}

type LoadingEleRule = React.ReactNode;

interface GuardRule {
    routers : RouteObjectRule[],
    onRouterBefore ?: onRouteBeforeRule,
    loading ?: LoadingEleRule,
}

let onRouterBefore : onRouteBeforeRule;
let RouterLoading : FunctionRule;

//路由导航,设置redirect重定向 和 auth权限
function Guard ({ element, meta}) {
    const { pathname } = useLocation();
    const nextPath = onRouterBefore ? onRouterBefore(meta ,pathname) : pathname;
    if (nextPath && nextPath !== pathname) {
        element = <Navigate to={nextPath} replace={true}/>;
    }
    return element;
}


// 路由懒加载
function lazyLoadRouters(page, meta){
    meta = meta || {};
    const LazyElement = React.lazy(page);
    const GetElement = ()=> {
        return (
            <Suspense fallback={<RouterLoading/>}>
                <LazyElement/>
            </Suspense>
        );
    };
    return <Guard element={<GetElement/>} meta={meta}/>;
}

function transRoutes(routes : RouteObjectRule[]) {
    const list = [];
    routes.forEach(route => {
        const obj = { ...route };
        if (obj.redirect) {
            obj.element = <Navigate to={obj.redirect} replace={true} />
        }
        if (obj.page) {
            obj.element = lazyLoadRouters(obj.page, obj.meta)
        }
        if (obj.children) {
            obj.children = transRoutes(obj.children)
        }
        ['redirect','page','meta'].forEach(name => delete obj[name]);
        list.push(obj)
    });
    return list
}

export type {
    RouteObjectRule,
    MetaRule,
    FunctionRule,
    onRouteBeforeRule,
    LoadingEleRule,
}

function RouterGuard(params : GuardRule){
    onRouterBefore = params.onRouterBefore;
    RouterLoading = ()=> params.loading || <></>;
    return useRoutes(transRoutes(params.routers));
}
export default RouterGuard;


3:app.ts , 引入封装的routerdom6guard,传入router数组

import React from 'react';
import routes, {onRouteBefore} from "./routers";
import {BrowserRouter, Route} from "react-router-dom";
import {setLocalStorage, getLocalStorage} from "./utils";
import store, {actions} from './redux';
import RouterGuard from './react-router-dom6-guard';
import Loading from './components/Loading';

const App = function () {
    const UserInfo: any = getLocalStorage('userInfo');
    const access_token: any = getLocalStorage('access_token');
    if (UserInfo) {
        store.dispatch(actions.setUserInfo(JSON.parse(UserInfo)));
        store.dispatch(actions.setToken(access_token));
    }
    window.addEventListener('beforeunload', (event) => {
        setLocalStorage('userInfo', JSON.stringify(store.getState().userInfo));
        setLocalStorage('access_token', store.getState().access_token);
    });
    const baseUrl = `/${process.env.PUBLIC_URL}` || '/'; //路由前缀 = 打包路径 如:uauth, 解决打包项目后,刷新404等问题
    return (
        <BrowserRouter basename={baseUrl}>
            <RouterGuard
                routers={routes}
                onRouterBefore={onRouteBefore}
                loading={<Loading/>}
            />
        </BrowserRouter>
    );
};
export default App;

4:index.ts ,没啥特殊操作

import React from 'react';
import ReactDOM from 'react-dom';
import zhCN from 'antd/lib/locale/zh_CN';
import './assets/ali-fonts/iconfont.css';
import 'antd/dist/antd.less';
import './css/style.scss';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './redux';
import {Provider} from 'react-redux';
import {ConfigProvider} from 'antd';


ReactDOM.render(
    <Provider store={store}>
        <ConfigProvider locale={zhCN}>
            <App/>
        </ConfigProvider>
    </Provider>,
    document.getElementById("root"),
);

reportWebVitals();


5:.env文件配置 , 可以解决打包项目后,刷新404等问题

 PUBLIC_URL = 'uauth'   //设置为打包的文件夹名称,也是路由前缀路由,加在BrowserRouter /HashRouter的basename上

6:项目未写完,需要请酌情copy

项目下载地址

 类似资料: