• react路由集中管理及鉴权 reactrouterconfig


    react路由集中管理及鉴权 react-router-config

     

    git地址:https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config
    react-router-config主要用来帮助我们进行集中式路由的配置,在不使用react-router-config之前,我们的路由使用react-router-dom库来进行配置,类似如下代码:

    import React from 'react';
    import {
      BrowserRouter as Router,
      Switch,
      Route,
    } from "react-router-dom";
    import Home from './pages/Home';
    import Login from './pages/Login';
    import Backend from './pages/Backend';
    import Admin from './pages/Admin';
    
    function App() {
      return (
        <Router>
          <Switch>
            <Route path="/login" component={Login}/>
            <Route path="/backend" component={Backend}/>
            <Route path="/admin" component={Admin}/>
            <Route path="/" component={Home}/>
          </Switch>
        </Router>
      );
    }
    
    export default App;
    

    react-router-config可以使得路由配置更加简便

    routes.ts路由配置

    import { RouteConfig } from 'react-router-config';
    import Home from '../components/Home';
    import Inquiry from '../components/Inquiry';
    import Offer from '../components/Offer';
    
    const routes: RouteConfig = [
        {
            path: '/',
            component: Home,
            routes: [
                {
                    path: '/inquiry',
                    component: Inquiry
                },
                {
                    path: '/offer',
                    component: Offer
                }
            ]
        }
    ]
    
    export default routes
    

    index.js入口文件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'mobx-react';
    import './index.css';
    import { renderRoutes } from 'react-router-config';
    import { HashRouter } from 'react-router-dom';
    import routes from './config/routes';
    import stores from './stores/index';
    import * as serviceWorker from './serviceWorker';
    
    ReactDOM.render(
     <HashRouter>
         <Provider {...stores}>
            {renderRoutes(routes)}
         </Provider>
     </HashRouter>,
     document.getElementById('root')
    );
    
    // If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: https://bit.ly/CRA-PWA
    serviceWorker.unregister();
    

    Home组件:

    import React from 'react';
    import { renderRoutes } from 'react-router-config';
    
    const Home = (props: any) => { 
         const route = props.route;
    
         return <div>
             <div>this is Home page.</div>
             {route && renderRoutes(route.routes)}
         </div>
    }
    
    export default Home;
    

    Inquiry组件:

    import React from 'react';
    
    const Inquiry = () => {
        return <div>Inquiry</div>
    }
    
    export default Inquiry;
    

    Offer组件

    import React from 'react';
    
    const Offer = () => {
        return <div>Offer</div>
    }
    
    export default Offer;
    

    下面代码实现了react-router-config的功能

    1.实现集中式路由配置

    router.js

    import React, { Suspense, lazy } from 'react'
    import { Route, Switch, Redirect } from 'react-router-dom'
    import { matchPath, Router } from "react-router"
    
    // component: React.lazy(() => import("pages/table-poc"))
    // component: lazy(() => import("@/pages/user/All"))
    // component: require('../a/b/index.js').default,
    
    //const Game = React.lazy(() => new Promise( (resolve) => {
    //    setTimeout( () => resolve(import('./Game')) , 1000)
    //}))
    // 使用React自带的 Suspense,lazy实现懒加载
    const routes = [
        { path: "/", exact: true, render: () => <Redirect to={"/login/password"} /> },
        { path: "/login", exact: true, render: () => <Redirect to={"/login/password"} /> },
        { path: "/principal", exact: true, render: () => <Redirect to={"/principal/teacher"} /> },
        { path: "/teacher", exact: true, render: () => <Redirect to={"/teacher/textbook"} /> },
        {
            path: '/login/:type',
            name: 'Login',
            component: lazy(() => import("pages/login/login.jsx")),
            meta: { title: '登录' }
        },
        {
            path: '/password',
            name: 'Login',
            component: lazy(() => import("pages/login/login.jsx")),
            meta: { title: '修改密码' }
        },
    
        {
            path: '/teacher',
            name: 'Teacher',
            component: lazy(() => import('pages/layout/layout.jsx')),
            meta: { title: '教师端' },
            routes: [
                {
                    path: '/teacher/textbook',
                    name: 'TeacherTextbook',
                    component: lazy(() => import('pages/teacher/textbook/textbook.jsx')),
                    meta: { title: '选课' },
                    routes: [
                        {
                            path: '/teacher/textbook/index',
                            name: 'TeacherTextbookIndex',
                            component: lazy(() => import('pages/test.jsx')),
                            meta: { title: '测试啊' },
                        }
                    ]
                },
                {
                    path: '/teacher/in-class/:id/:name',
                    name: 'TeacherInclass',
                    component: lazy(() => import('pages/teacher/in-class/in-class.jsx')),
                    meta: { title: '上课' }
                }
            ]
        }
    ]
    
    // 实现react-router-config里的renderRoutes方法
    function renderRoutes (routes, extraProps = {}, switchProps = {}) {
        return routes ? (
            <Suspense fallback={<div>页面加载中...</div>}>
                <Switch {...switchProps}>
                    {routes.map((route, i) => (
                        <Route
                            key={route.key || i}
                            path={route.path}
                            exact={route.exact}
                            strict={route.strict}
                            render={props =>
                                route.render ? (
                                    route.render({ ...props, ...extraProps, route: route })
                                ) : (
                                    <route.component {...props} {...extraProps} route={route} />
                                )
                            }
                        />
                    ))}
                </Switch>
            </Suspense>
        ) : null;
    }
    
    // 实现react-router-config里的matchRoutes方法
    function matchRoutes (routes, pathname, /*not public API*/ branch = []) {
        routes.some(route => {
            const match = route.path
                ? matchPath(pathname, route)
                : branch.length
                    ? branch[branch.length - 1].match // use parent match
                    : Router.computeRootMatch(pathname); // use default "root" match
            
                    if (match) {
                branch.push({ route, match });
    
                if (route.routes) {
                    matchRoutes(route.routes, pathname, branch);
                }
            }
            return match;
        });
        return branch;
    }
    
    export { routes, renderRoutes, matchRoutes }
    

    App.js

    import './App.scss';
    import { BrowserRouter } from 'react-router-dom'
    import { routes, renderRoutes } from './router'
    
    function App () {
      return (
        <BrowserRouter>
          {/* 这个方法,每次有子路由时都需要使用,会传当前路由的子路由,可以说是按需加载,
           实时编译的,而不是一次性吧所有路由都渲染出来 */}
          {renderRoutes(routes)}
        </BrowserRouter>
      )
    }
    
    export default App
    

    其他页面调用时:{renderRoutes(this.props.route.routes)}

    2.实现面包屑
    import React, { Component } from 'react'
    import { Link } from 'react-router-dom'
    import { renderRoutes, matchRoutes, routes } from 'router'
    
    export default class Layout extends Component {
        render () {
            const branch = matchRoutes(routes, this.props.location.pathname)
            return (
                <div>
                    <header>
                        {/* 面包屑 */}
                        {
                            branch.map((item, i) => {
                                return item.match.url === pathname ?
                                    <span key={i}>{item.route.meta.title}</span> :
                                    <Link to={item.match.path} key={i} className='back'>{item.route.meta.title}</Link>
                            })
                        }
                    </header>
                    {/* 路由 */}
                    {renderRoutes(nowRoutes)}
                </div>
            )
        }
    }
    

    3.路由鉴权
    以下是个人重写,添加了多重权限验证以及多重路由匹配(即添加渲染非<Switch>包裹的<Route>)。

    export interface IRouteConfig extends RouteConfig {
      auth?: number;
      routes?: IRouteConfig[];
      multipleRoutes?: IRouteConfig[];
    }
    
    /**
     * 将路由配置渲染成节点
     * @param routes switch路由列表
     * @param authed 当前账号权限
     * @param multipleRoutes 非switch路由列表,将会在Switch节点前渲染Route
     * @param extraProps 添加额外的Route props
     * @param switchProps Switch props
     */
    function renderRoutes(
      routes: IRouteConfig[] | undefined,
      authed: number,
      multipleRoutes?: IRouteConfig[],
      extraProps?: any,
      switchProps?: SwitchProps
    ) {
      const isMobile = checkMobile();
      let list = [];
      const mapFunc = (R: IRouteConfig[]) =>
        R.map((route, i) => (
          <Route
            key={route.key || i}
            path={route.path}
            exact={route.exact}
            strict={route.strict}
            render={props => {
              // 将authed赋值到route,试子组件可以通过route.authed获取当前用户权限
              if (authed !== undefined) route.authed = authed;
              // 不存在authed或者authed大于当前路由权限,即可渲染组件,否则跳转登录界面
              if (!route.auth || route.auth <= authed) {
                return route.render
                  ? route.render({ ...props, ...extraProps, route: route })
                  : route.component && (
                      <route.component {...props} {...extraProps} route={route} />
                    );
              } else {
                message.warn("请先登录!");
                return (
                  <Redirect to={route.auth <= 1 "/user/loginRegister/login"} />
                );
              }
            }}
          />
        ));
      if (routes) {
        list.push(
          <Switch {...switchProps} key="biubiubiu~~">
            {mapFunc(routes)}
          </Switch>
        );
        // 将非Switch包裹的Route挂载到Switch节点之前
        multipleRoutes && list.unshift(...mapFunc(multipleRoutes));
        // 返回一个数组,[<Route/>,...,<Route/>,<Switch>...</Switch>](实际元素并非如此结构,此处仅为方便说明写的伪代码),React会将数组渲染成节点
        return list;
      }
    }
    

    修改后的routes,当匹配到"/user/all/article/(id值)"的路径,页面会同时渲染Article以及All两个组件,未登录则渲染Article和Login组件,mutipleRoutes主要是为了能通过多重匹配路由模拟VUE的keep-alive效果。代码如下:

    export const mobileRouterList: IRouteConfig[] = [
      {
        path: "/",
        exact: true,
        render: () => <Redirect to="/user/all" />
      },
      {
        path: "/user",
        component: MobileMain,
        multipleRoutes: [
          { path: "/user/:page/article/:id", component: Article }
        ],
        routes: [
          {
            path: "/user/all",
            component: lazy(() => import("@/pages/user/All"))
          },
          {
            path: "/user/me",
            auth: 1, // 用户权限必须 >=1 才可以访问
            component: lazy(() => import("@/pages/user/Me"))
          },
          {
            path: "/admin/controlPanel",
            auth: 2, // 用户权限必须 >=2(管理员) 才可以访问
            component: lazy(() => import("@/pages/admin/ControlPanel"))
          }
        ]
      }
    ]
    

    修改后的rednerRoutes使用,后面参数选填:

    // 顶层使用路由渲染,手动添加第二参数
    renderRoutes(routes,user.authed)
    // 子层组件路由渲染
    renderRoutes(props.route.routes,<props.route.authed>,<props.route.multipleRoutes>,...)
    

    以下是自己封装的路由鉴权(判断是否登录以及页面的访问权限)

    import React, { Suspense, lazy } from 'react'
    import { Redirect } from 'react-router-dom'
    import { Route, Switch } from 'react-router'
    // exact属性为true时路径中的hash值必须和path完全一致才渲染对应的组件,如果为false则'/'也可以匹配'/login';
    // (如果strict属性为false,则末尾是否包含反斜杠结尾不影响匹配结果)
    
    // strict属性主要就是匹配反斜杠,规定是否匹配末尾包含反斜杠的路径,如果strict为true,则如果path中不包含反斜杠结尾,
    // 则他也不能匹配包含反斜杠结尾的路径,这个需要和exact结合使用
    
    const permissions = 'user'//登录接口获取的当前用户的角色
    const requiresAuth = true //是否已经登录
    
    //所有的路由
    export const routes = [
      {
        path: '/',
        exact: true,
        requiresAuth: false,
        permissions: ['user', 'admin'],
        render: () => <Redirect to='/login' />
      },
      {
        path: '/login',
        name: 'Login',
        exact: true,
        strict: true,
        requiresAuth: false,//是否需要登录
        permissions: ['user', 'admin'], // 当前登录权限必须 user或admin 才可以访问
        component: lazy(() => import('../pages/login.jsx')),//路由懒加载
        meta: { title: '登录呢', icon: 'login' }
      },
      {
        path: '/JsPlan1',
        name: 'JsPlan1',
        exact: true,
        requiresAuth: true,
        permissions: ['user', 'admin'],
        component: lazy(() => import('../pages/js/plan1.jsx')),
        meta: { title: 'js的plan1', icon: 'user' }
      },
      {
        path: '/JsPlan2',
        name: 'JsPlan2',
        exact: true,
        requiresAuth: true,
        permissions: ['admin'],
        component: lazy(() => import('../pages/js/plan2.jsx')),
        meta: { title: 'js的plan2' }
      },
      {
        path: '/TsPlan1',
        name: 'TsPlan1',
        exact: true,
        requiresAuth: true,
        permissions: ['admin'],
        component: lazy(() => import('../pages/ts/plan1.tsx')),
        meta: { title: 'ts的plan1' }
      },
      {
        path: '/TsPlan2',
        name: 'TsPlan2',
        exact: true,
        requiresAuth: true,
        permissions: ['user', 'admin'],
        component: lazy(() => import('../pages/ts/plan2.tsx')),
        meta: { title: 'ts的plan2' }
      },
    ]
    
    export const renderRoutes = (routes, extraProps = {}, switchProps = {}) => {
      return routes ? (
        <Suspense fallback={<div>页面加载中...</div>}>
          <Switch {...switchProps}>
            {routes.map((item, i) => (
              <Route
                key={item.name || i}
                path={item.path}
                exact={item.exact}
                strict={item.strict}
                render={props => routeRender(item, props, extraProps)}
              />
            ))}
          </Switch>
        </Suspense>
      ) : null
    }
    
    const routeRender = (route, props, extraProps) => {
      // 登录判断(需要登录 && 未登录-->跳登录页面,,,,,,,不需要登录 || 已经登录-->正常跳转)
      const login = route.requiresAuth && !requiresAuth //跳登录
      // 该角色是否有权限访问该页面(当前角色是否在 路由要求角色数组中)
      const auth = route.permissions.includes(permissions) //有权限
      console.log(222, '是需要跳转登录页面' + login, '是否有权限' + auth, props)
      // 判断渲染route
      if (login) {
        return <Redirect to={{ path: '/login', message: '请登录后再操作!' }} />
      } else {
        if (auth) {
          return route.render ? (
            route.render({ ...props, ...extraProps, route: route })
          ) : (
            <route.component {...props} {...extraProps} route={route} />
          )
        } else {
          alert('您暂无权限')
          return <Redirect to={{ path: '/login', message: '请登录后再操作!' }} />
        }
      }
    }
  • 相关阅读:
    【LeetCode】145. Binary Tree Postorder Traversal (3 solutions)
    【LeetCode】151. Reverse Words in a String
    【LeetCode】152. Maximum Product Subarray
    【LeetCode】154. Find Minimum in Rotated Sorted Array II (3 solutions)
    如何使用 Chrome 浏览器调试动态加载的 Javascript 脚本
    如何对数据库中的表以及表中的字段进行重命名
    关于 sql server 数据库权限乱七八糟的一些东西
    对 sql server 数据库的备份进行加密
    使用vs的查找功能,简单大概的统计vs中的代码行数
    在 sql server 中,查询 数据库的大小 和 数据库中各表的大小
  • 原文地址:https://www.cnblogs.com/songfengyang/p/16286945.html
Copyright © 2020-2023  润新知