• 尝试 React16、React-router4 实现根据动态菜单生成按需加载的路由


    1. 因为 react-router4 没有在提供 onEnter 这样的全局跳转钩子,所以要通过 高阶组件 去处理 来实现一个 路由守卫

    2. 按需加载这里我同样使用 高阶组件 来实现

    3. 登录成功时 要先获取用户菜单保存到redux中,在登录状态下,刷新页面需要重新获取菜单,并保存到redux中,方便在路由守卫中直接从redux中拿到菜单数据来填充进主体页面路由文件中去。

    ps: 只在这里记录一下尝试的核心代码部分,实际效果可在  github  上克隆代码后 运行项目查看

    一、路由守卫   

    守卫 routerComponent.js相关代码
      1 import React, { Component } from 'react';
      2 
      3 import { Route, Redirect } from 'react-router-dom';
      4 
      5 import { renderRoutes } from 'react-router-config';
      6 
      7 import {asyncComponent as async} from '@/utils/asyncComponent.js';
      8 
      9 import store from '@/store/index';
     10 
     11 import Main from '@/router/main';
     12 
     13 
     14 class RouterAuth extends Component {
     15     constructor(props) {
     16         super(props);
     17 
     18         this.state = {
     19 
     20         };
     21     }
     22 
     23     //根据菜单生成路由文件
     24     handleRouters(menu){
     25         let childRouter = [];
     26         menu.forEach((item) => {
     27             if(!!item.childs){
     28                 childRouter = [...childRouter, ...this.handleRouters(item.childs)];
     29             }else{
     30                 let component = item.component;
     31                 let path = item.path;
     32                 //根据es6module语法,由于import是静态执行,所以不能使用表达式和变量,
     33                 //解决方法 es6模板字符串 import(`./path/${myFile}.jsx`)。
     34                 // 注意:
     35                 // ${myFile}变量前边一定要写一个带"/"的字符串。
     36                 // ".jsx" 不能写在变量里,要写在字符串里。
     37                 //目前只能一个页面对应一个js,如何按模块对应js?
     38                 item.component = async(()=>import(/* webpackChunkName: "[request]" */ `@/${component}.jsx`));
     39                 return childRouter.push(item)
     40             }
     41         })
     42         return childRouter
     43     }
     44 
     45     render() {
     46         let { location, config } = this.props;
     47         let { pathname } = location;
     48         console.log(location, config, pathname)
     49 
     50         let token = localStorage.getItem('token');
     51 
     52         let targetRouterConfig = config.find((item) => item.path === pathname);
     53 
     54 
     55         //如果是登录状态
     56         if(!!token){
     57             //如果进入登录页面,则直接重定向至首页
     58             if(pathname === '/login' || pathname === '/'){
     59                 return <Redirect to='/home' />
     60             }else{
     61                 //如果路由存在
     62                 if(targetRouterConfig){
     63                     //如果是需要登录的或者是404页面则直接进入
     64                     if(targetRouterConfig.auth || pathname === '/404'){
     65                         let { component } = targetRouterConfig;
     66                         return <Route exact path={pathname} component={component} />
     67                     }else{//否则重定向到首页
     68                         return <Redirect to='/home' />
     69                     }
     70                 }else{
     71                     //判断没有设置权限菜单,则根据菜单设置上
     72                     if(Main[0].routes.length == 0){
     73                         let menu = store.getState().user.menu;
     74                         let menus = this.handleRouters(menu);
     75                         Main[0].routes = menus;
     76                     }
     77                     //如果菜单中包含当前路由,则进入
     78                     let menuConfig = Main[0].routes.filter((item) => {
     79                         return item.path === pathname
     80                     });
     81                     if(menuConfig.length != 0){
     82                         return renderRoutes(Main);
     83                     }else{//不包含则进入404
     84                         return <Redirect to='/404' />
     85                     }
     86 
     87                 }
     88             }
     89         }else{ //非登录状态
     90             //如果路由存在
     91             if(targetRouterConfig){
     92                 //如果需要登录,则跳转到登录页
     93                 if(targetRouterConfig.auth){
     94                     return <Redirect to='/login' />
     95                 }else{//不需要登录,则正常进入
     96                     let { component } = targetRouterConfig;
     97                     return <Route exact path={pathname} component={component} />
     98                 }
     99             }else{
    100                 //路由不存在,直接进入登录页
    101                 return <Redirect to='/login' />
    102             }
    103         }
    104     }
    105 }
    106 
    107 export default RouterAuth;
    View Code

       路由 index.js文件代码

     1 import React from 'react';
     2 
     3 import { HashRouter, Switch, Route, Redirect } from "react-router-dom";
     4 
     5 import RouterAuth from '@/utils/routerComponent';
     6 
     7 /*
     8 HashRouter
     9 1.用这个了就代表路径加上/#/
    10 2.换成BrowserRouter了;路径就不需要加/#/
    11 3.用HashRouter了就要把path的路径名字带上,如果首次加载默认的话要这样写: <Route exact path="/"  component={App}/>
    12 */
    13 
    14 import Login from './login';
    15 
    16 import Main from './main';
    17 
    18 import NotFound from './notFound';
    19 
    20 const routes = [
    21     ...Login,
    22     ...NotFound
    23 ];
    24 
    25 const BasicRoute = () => (
    26     <HashRouter>
    27         <Switch>
    28             <RouterAuth config={routes}></RouterAuth>
    29         </Switch>
    30     </HashRouter>
    31 );
    32 
    33 export default BasicRoute;
    View Code

      

    二、按需加载

    按需加载 asyncComponent.js 代码

     1 import React from 'react'
     2 
     3 export const asyncComponent = loadComponent => (
     4     class AsyncComponent extends React.Component {
     5         constructor(props) {
     6             super(props);
     7             this.state = {
     8                 Component: null
     9             }
    10         }
    11 
    12         UNSAFE_componentWillMount() {
    13             if (this.hasLoadedComponent()) {
    14                 return
    15             }
    16 
    17             loadComponent()
    18                 .then(module => module.default)
    19                 .then((Component) => {
    20                     this.setState({Component})
    21                 })
    22                 .catch((err) => {
    23                     console.error(`Cannot load component in <AsyncComponent />`);
    24                     throw err
    25                 })
    26         }
    27 
    28         hasLoadedComponent() {
    29             return this.state.Component !== null
    30         }
    31 
    32         render() {
    33             const {Component} = this.state;
    34             return (Component) ? <Component {...this.props} /> : null
    35         }
    36     }
    37 )
    View Code

    三、获取菜单相关

    登录成功时代码

     1 //登录
     2     onFinish = (values) => {
     3         api.login(values).then((result) => {
     4             //登录成功后需要先获取下菜单,然后在跳转至主页
     5             //获取个人信息与系统菜单
     6             common.getMenu().then((res) => {
     7                 if(res.success){
     8                     store.dispatch({
     9                         type: 'USER_MENU',
    10                         data: res.data.menu
    11                     })
    12                     localStorage.setItem('token', result.token);
    13                     this.props.history.push('/home');
    14                 }
    15             })
    16         })
    17     }
    View Code

    刷新页面时代码

     1 //如果是登录状态,则刷新页面时需先获取到菜单后才能挂载路由页面
     2 let token = localStorage.getItem('token');
     3 if(!!token){
     4     //获取个人信息与系统菜单
     5     api.getMenu().then((res) => {
     6         if(res.success){
     7             store.dispatch({
     8                 type: 'USER_MENU',
     9                 data: res.data.menu
    10             })
    11 
    12             ReactDOM.render(
    13                 <Router />,
    14                 document.getElementById('root')
    15             );
    16         }
    17     })
    18 }else{
    19     ReactDOM.render(
    20         <Router />,
    21         document.getElementById('root')
    22     );
    23 }
    View Code
  • 相关阅读:
    工厂增强
    面试题
    SpringBean生命周期及作用域
    字符串
    带参数方法实例
    带参数方法
    人机猜拳
    类的无参方法
    类和对象实例2
    类和对象实例1
  • 原文地址:https://www.cnblogs.com/dibaosong/p/12661274.html
Copyright © 2020-2023  润新知