嘗試 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM