reactRouter路由守卫与最佳实践

本文介绍了如何在reactRouter中实现路由守卫功能,包括基本的路由保护、多重权限控制、按需加载路由模块以及优化路由配置,详细阐述了各个阶段的实现思路和代码示例。
摘要由CSDN通过智能技术生成

 
 

前言

路由守卫是开发中很常用的功能,然而reactRouter是没有提供路由守卫的api的,只能由我们自己实现。我会先实现路由守卫的基本功能,然后逐步完善它来为reactRouter增添更多功能。我想结合自己对代码的迭代过程来对代码做介绍。
阶段一:实现基本功能
阶段二:多重权限(角色)
阶段三:按需加载路由模块
阶段四:优化路由配置信息的书写格式并实现路由别名功能

如果您时间不多或对我的迭代过程并不感兴趣的话,可以直接从这个地址clone项目源码:https://gitee.com/yangguang110/react-navigation-guards 这里有项目最新的代码。
或者访问项目主页

项目目录

src
├── router
│   ├── index.ts         #入口文件
│   ├── route.config.ts  #路由配置
│   ├── Guards.tsx       #路由守卫组件
│   └── NotFound.tsx     #404页面
├── view
│ 	├── console
│ 	│	└──  index.tsx
│ 	├── login
│ 	│	└──  index.tsx
│ 	├── main
│ 	│	└──  index.tsx
│ 	├── news
│ 	│	└──  index.tsx
│ 	└── user-manage
│ 		└──  index.tsx
└── utils
 	└── index.tsx

阶段一

这个阶段我想做到以下几点:

  1. 根据路由配置动态生成嵌套的路由
  2. 对路由增加访问权限,个别页面只能允许已经登陆的用户访问。权限不足时跳转到登录页面。
  3. 实现404页面

结合个例子说明。比如,我现在想给一个后台管理系统实现路由,这个后台管理系统有5个页面,分别是控制台(console)、登录页(login)、新闻(news)、用户管理(userManage)、main(控制台主页),其中news、userManage以及main页面都是console的子页面(嵌在console页面内)。除了login之外的四个页面都需要登录后才允许访问。

因此,逻辑是这样的:
如果用户访问不存在的页面则跳转到404。
如果用户未登录,那么访问login页面可以正常访问,而访问其他四个页面会跳转到登录页。
如果用户已经登陆,那么访问login页面会跳转到控制台主页,否则跳转到用户所访问的页面。

下面开始编写代码,因为我打算根据路由配置信息来动态创建路由,所以先编写配置文件。

// src/router/route.config.ts
import Main from "@/view/main"
import Login from "@/view/login"
import News from "@/view/news"
import Console from "@/view/console"
import UserManage from "@/view/user-manage"

export interface RouteModel {
   
    path: string,
    component: object,
    auth: boolean,
    childRoutes?: RouteModel[]
}

/* 
    这里只需配置好这些路由的相互关系即可
    比如login和console是同级(兄弟),而news是console的子路由(父子)
*/
export const routeConfig: RouteModel[] = [
    {
   
        path: "/login",
        component: Login,
        auth: false  // false表示不需要登录即可访问
    },
    {
   
        path: "/console",
        component: Console,
        auth: true,
        childRoutes: [
            {
   
                path: "/console/main",
                component: Main,
                auth: true
            },
            {
   
                path: "/console/news",
                component: News,
                auth: true
            },
            {
   
                path: "/console/userManage",
                component: UserManage,
                auth: true
            }
        ]
    }
]

然后是路由守卫组件,贴源码之前说一下思路。
首先,当我们使用react-router-dom提供的Switch组件将自定义组件包裹起来之后,自定义组件的props中会被注入一些与路径有关的信息,这部分数据的原型如下:

interface RouteProps {
   
    location?: H.Location;
    component?: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
    render?: (props: RouteComponentProps<any>) => React.ReactNode;
    children?: ((props: RouteChildrenProps<any>) => React.ReactNode) | React.ReactNode;
    path?: string | string[];
    exact?: boolean;
    sensitive?: boolean;
    strict?: boolean;
}

其中props.location.pathname表示当前用户访问的目标路径,在路由守卫中会根据这个来判断应该渲染哪一个组件。

其次,读者需要知道reactRouter是如何实现嵌套路由的,这个可以参考官方示例。以及如何给子路由传值

最后,必须明确的是,如果我们的路由配置信息中有嵌套路由,那么路由守卫就不可能是一个,每一层路由都会对应一个路由守卫,这些路由守卫之间唯一的区别就是他们需要渲染的路由列表不同。管理第一层路由的守卫需要判断何时该渲染login和console,管理第二层的路由的守卫则判断何时渲染news、userManage和main。
举个现实例子:如果我们访问/console/news,那么第一层的路由守卫是无法去渲染News组件的,因为它拿不到相应的配置信息,有关/console/news这个路径的配置是在第二层的路由守卫手里的,但是第一层的路由守卫却有/console这个路径的信息,于是它只需要把Console组件渲染出来即可。因为News、Main和UserManage都是Console的子路由,所以我们把第二层的路由守卫设置在Console组件内,当Console组件被渲染出来之后,其上的第二层路由路由守卫继续判断/console/news这个路径,发现这个路径正好是自己管理的,于是将对应的News组件渲染在页面上。

源码如下:

// src/router/Guards.tsx
import React, {
    Component } from 'react';
import {
    RouteProps } from "react-router"
import {
    Route, Redirect } from "react-router-dom"
import NotFound from "./NotFound"
import {
    RouteModel } from "./route.config"
import {
    permission } from "@/store/mobx"

export interface NavigationGuardsProps extends RouteProps {
   
    routeConfig: RouteModel[]
}
class NavigationGuards extends Component<NavigationGuardsProps, any> {
   

    /**
     * 判断一个路径是否是另一个路径的子路径
     * @param pathConfig 配置文件中的路径
     * @param pathTarget 用户请求的路径
     */
    static switchRoute(pathConfig:string,pathTarget:string){
   
        
        if(pathConfig===pathTarget) return true;

        const reg=new RegExp(`(^${
     pathConfig})(?=/)`)
        return reg.test(pathTarget)
    }

    render() {
   
        const {
    location, routeConfig } = this.props
        const targetPath: string | undefined = location && location.pathname
        const targetRoute: RouteModel | undefined = routeConfig.find((item) =>
            targetPath&&NavigationGuards.switchRoute(item.path,targetPath))
        
        const isLogin = permission.isLogin
        if (!targetRoute) {
   
            return <NotFound></NotFound>
        }

        if (isLogin) {
   
            return <LoginHandler targetRoute={
   targetRoute}></LoginHandler>
        } else {
   
            return <NotLoginHandler targetRoute={
   targetRoute}></NotLoginHandler>
        }
    }
}

//已经登陆的状态下,处理路由
function LoginHandler(props): any {
   
    const {
    targetRoute } = props
    const {
    path } = targetRoute
    if (path === 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值