尝试 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

 

posted @ 2020-04-08 17:38  年少、  阅读(3461)  评论(3编辑  收藏  举报