React Router 模拟一个导航守卫

342 阅读4分钟

1、创建文件

我们在 /src/router 目录中创建一个 RouterGuards.jsx 文件作为导航守卫的配置文件。

然后在 src 目录中新建一个 config 的目录,然后在该目录中创建一个 routes.js 文件,用来设置项目中涉及到路由相关数据。

2、配置一级路由数据

我们首先先来处理用户是否登录的权限问题。

我们在 routes.js 文件中添加项目中所有一级路由相关的数据:

import Login from "../pages/login/Login"
import Register from "../pages/register/Register"
import Home from "../pages/home/Home"
const routes = [    
    { path: '/login', component: Login }, 
    { path: '/register', component: Register },
    { path: '/404', component: NotFound }, 
    { path: '/', component: Home, isLogin: true }
    ]
export default routes;

其中,isLogin: true 属性用来表示当前路由需要登录才能访问。

3、渲染一级路由,处理 404

在 RouterGuards.jsx 文件中,渲染一级路由的配置:

  1. 获取用户访问的路径;
  2. 然后判断用户访问的路径在项目中是否存在;
  3. 如果存在,则生成当前路由的配置代码;
  4. 如果不存在,则直接重定向到 404 页面;
import React from 'react'
import { Redirect, Route, useLocation } from 'react-router-dom';// 项目中所有的路由数据import routes from '../config/routes'
export default function RouterGuards() {    // 1. 获取当前用户访问的路由地址
    const location = useLocation();
    const userPath = location.pathname; 
    // 2. 判断当前用户访问的路由地址在项目中是否存在
    const userRoute = routes. find(item => item.path === userPath);
    if (userRoute) {
    // 3. 进入 if,则表示用户访问的路由是项目中存在的路由
    return <Route path={userRoute.path} component={userRoute.component}></Route>
    } else {
    // 4. 进入 else,则表示用户访问的路由在项目中不存在
    return <Redirect to="/404"></Redirect> 
    }
}

然后在 RootRouter.jsx 中引入该配置组件:

export default function RootRouter() {
    return (
        <React.Suspense fallback={<h1>加载中...</h1>}>
            <BrowserRouter> 
                <Switch>                    
                    <RouterGuards></RouterGuards>
                </Switch> 
            </BrowserRouter>
        </React.Suspense>
    )
}

4、处理未登录访问路由

export default function RouterGuards() {
    // 1. 获取当前用户访问的路由地址
    const location = useLocation();
    const userPath = location.pathname;
    // 2. 判断当前用户访问的路由地址在项目中是否存在
    const userRoute = routes.find(item => item.path === userPath);
    if (userRoute) {
    // 3. 进入 if,则表示用户访问的路由是项目中存在的路由
    if (userRoute.isLogin) {
    // 6. 进入 if,说明用户当前访问的路由需要登录
    // 7. 判断用户是否登录(实际开发中,应该还需要发送请求验证 token 是否过期)
    const token = localStorage.token;
    if (token) {
    // 8. 进入 if,说明用户登录了,可以让用户去到想要访问的路由
    return <Route path={userRoute.path} component={userRoute.component}></Route>            } 
    // 9. 说明当前路由需要登录,但是用户没有登录
    return <Redirect to="/login"></Redirect>
    }
    // 10. 当前用户访问的路由存在,且不需要登录
    return <Route path={userRoute.path} component={userRoute.component}></Route>
    } else {
    // 4. 进入 else,则表示用户访问的路由在项目中不存在
    return <Redirect to="/404"></Redirect>
    }
}

5、配置二级路由数据

    const routes = [
        { path: '/login', component: Login },
        { path: '/register', component: Register },
        { path: '/404', component: NotFound },
        { path: '/', component: Home, isLogin: true, },
        { path: '/main', component: Home, isLogin: true, },
        { path: '/users', component: Home, isLogin: true, },
        { path: '/roles', component: Home, isLogin: true, },
        { path: '/products/categories', component: Home, isLogin: true, },
        { path: '/products/list', component: Home, isLogin: true },
        { path: '/products/add', component: Home, isLogin: true },
        { path: '/products/update', component: Home, isLogin: true },
        { path: '/finances/payment', component: Home, isLogin: true },
        { path: '/finances/sales', component: Home, isLogin: true }
        ]

6、渲染二级路由的父路由

import React from 'react'import { Redirect, Route, useLocation } from 'react-router-dom';
import { message } from 'antd';// 项目中所有的路由数据
import routes from '../config/routes'
export default function RouterGuards() {
    // 1. 获取当前用户访问的路由地址
    const location = useLocation();
    const userPath = location.pathname;
    // 2. 判断当前用户访问的路由地址在项目中是否存在
    const userRoute = routes.find(item => item.path === userPath);
    if (userRoute) {
    // 3. 进入 if,则表示用户访问的路由是项目中存在的路由
    // 5. 判断当前路由是否需要用户登录后才能访问
    if (userRoute.isLogin) {
    // 6. 进入 if,说明用户当前访问的路由需要登录
    // 7. 判断用户是否登录(实际开发中,应该还需要发送请求验证 token 是否过期)            const token = localStorage.token;
    if (token) { 
    // 8. 进入 if,说明用户登录了,可以让用户去到想要访问的路由
    // 当代码执行到这一步时,针对当前项目,说明当前用户访问的路径,要么是 /,那么就是 / 的子路由                // 11. 判断当前用户是否有权限访问当前路由
    // 12. 获取用户能够访问的路由的数据
    const roleMenus = JSON.parse(localStorage.menus || '[]'); 
    // 13. 判断当前用户访问的路径是否在 roleMenus 数组中
    if (roleMenus.includes(userPath) || userPath === '/') {
    return <Route path="/" component={userRoute.component}></Route>
    } 
    // 14. 用户没有权限访问当前路径
    return <Redirect to="/404"></Redirect>
    } 
    // 9. 说明当前路由需要登录,但是用户没有登录
    return <Redirect to="/login"></Redirect>
    }
    // 10. 当前用户访问的路由存在,且不需要登录
    return <Route path={userRoute.path} component={userRoute.component}></Route>
    } else {
    // 4. 进入 else,则表示用户访问的路由在项目中不存在 
    return <Redirect to="/404"></Redirect>
    }
}

7、处理动态路由

export default function RouterGuards() {
// ...
// 2. 判断当前用户访问的路由地址在项目中是否存在
    const userRoute = routes.find(item => {
    if (item.path === '/products/update') {
    return userPath.includes(item.path);
    }
    return item.path === userPath;
    }); 
    // ... 
    // 13. 判断当前用户访问的路径是否在 roleMenus 数组中
    if (roleMenus.includes(userPath) || userPath === '/' || userPath.includes('/products/update/')) {
    return <Route path="/" component={userRoute.component}></Route>
    }
}