react项目开发-路由优化(前三篇续)

react

目前的项目架构,大家都能看到,有路由跳转的地方都是写死的url,如<Link to={'/aaa'}>,push({pathname:'/login'}),这种方式呢,没有问题,但是这样写死不太好,不便于后期维护,

比如以后要改下路径,除了config.js中需要改一次,代码全篇幅都需要改一次,好累;name该怎么办呢?以下我们就来处理一下路由的优化!

优化思路:

最好能在一个地方去维护这些路径地址,也就是config.js中,name我们就需要把config中的menuGlobal存储一份到全局配置的store中,这样将来项目其他地方有用到路径的,均可以从tore中取出来使用即可。

那这里我们就考虑,存在store中的这路由数据,应该是什么结构呢,没错,我们使用immutable数据的Map形式,易操作,简单直观(之前我们在menuGlobal总预留的id和pid这里就要用到了)。具体如下:

1 修改utils/config.js如下:

[html] view plain copy

  1. import {OrderedSet,Map,fromJS} from 'immutable'  
  2.   
  3. const menuGlobal=[  
  4.     {  
  5.         id:'login',  
  6.         pid:'0',  
  7.         name:'登录',  
  8.         icon:'user',  
  9.         path: '/login',  
  10.         models: () => [import('../models/login')], //models可多个  
  11.         component: () => import('../routes/login'),  
  12.     },   
  13.     {  
  14.         id:'home',  
  15.         pid:'0',  
  16.         name:'首页',  
  17.         icon:'user',  
  18.         path: '/',  
  19.         models: () => [import('../models/home')], //models可多个  
  20.         component: () => import('../routes/home'),  
  21.     },   
  22.     {  
  23.         id:'aaa',  
  24.         pid:'0',  
  25.         name:'aaa页',  
  26.         icon:'user',  
  27.         path: '/aaa',  
  28.         models: () => [import('../models/aaa')], //models可多个  
  29.         component: () => import('../routes/AAA'),  
  30.     },   
  31.     {  
  32.         id:'bbb',  
  33.         pid:'0',  
  34.         name:'bbb页',  
  35.         icon:'user',  
  36.         path: '/aaa/bbb',  
  37.         models: () => [import('../models/bbb')], //models可多个  
  38.         component: () => import('../routes/BBB'),  
  39.     },   
  40.     {  
  41.         id:'ccc',  
  42.         pid:'0',  
  43.         name:'ccc页',  
  44.         icon:'user',  
  45.         path: '/ccc',  
  46.         models: () => [import('../models/ccc')], //models可多个  
  47.         component: () => import('../routes/CCC'),  
  48.     },   
  49. ];  
  50.   
  51. /**  
  52.  * 封装路由数据,利用id和pid的关联性处理  
  53.  */  
  54. const menuMap = (() => {  
  55.     let byId = Map();  
  56.     let byPid = Map();  
  57.     menuGlobal.map(item => {  
  58.       byId = byId.set(item.id, fromJS(item));  
  59.       byPid = byPid.update(item.pid, obj => obj ? obj.add(item.id) : OrderedSet([item.id]))  
  60.         
  61.     });  
  62.     return Map({  
  63.         byId,  
  64.         byPid  
  65.     });  
  66. })();  
  67.     
  68. export default {  
  69.     menuGlobal,  
  70.     menuMap  
  71. }  

2 修改models/app.js如下:

[html] view plain copy

  1. import {Map, fromJS} from 'immutable';  
  2. import {routerRedux} from 'dva/router';  
  3. import {config} from '../utils';  
  4. const {menuMap} = config;  
  5.   
  6. const initState = Map({  
  7.     i18n: 'zh_CN',  
  8.     token:null,  
  9.     locationPathname:null,  
  10.     menu:menuMap  
  11. })  
  12.   
  13. export default {  
  14.   
  15.     namespace: 'app',  
  16.     
  17.     state:initState,  
  18.     
  19.     subscriptions: {  
  20.         setup({ dispatch, history }) {  
  21.           
  22.         },  
  23.         setupHistory ({ dispatch, history }) {  
  24.             history.listen((location) => {  
  25.                 dispatch({  
  26.                     type: 'updateLocation',  
  27.                     payload: {  
  28.                       locationPathname: location.pathname  
  29.                     },  
  30.                 });  
  31.                 dispatch({  
  32.                     type: 'updateToken',  
  33.                     payload: {  
  34.                       token: window.sessionStorage.getItem('token')  
  35.                     },  
  36.                 })  
  37.             })  
  38.         },  
  39.     },  
  40.     
  41.     effects: {  
  42.   
  43.         * changeLang ({  
  44.             payload: {value},  
  45.         }, { put }) {  
  46.             yield put({ type: 'updateLang', payload: {value}});  
  47.         },  
  48.   
  49.         * updateLocation ({  
  50.             payload  
  51.         }, {put, select}) {  
  52.             yield put({type: 'updateStore', payload});  
  53.         },  
  54.   
  55.         * updateToken ({  
  56.             payload  
  57.         }, {put, select}) {  
  58.             yield put({type: 'updateStore', payload});  
  59.         },  
  60.   
  61.         * loginOk ({  
  62.             payload  
  63.         }, {put, select}) {  
  64.             window.sessionStorage.setItem('token',payload.token);  
  65.             yield put(routerRedux.push({  
  66.                 pathname: '/'  
  67.             }));  
  68.         },  
  69.   
  70.         * logout ({  
  71.             payload  
  72.         }, {put, select}) {  
  73.             window.sessionStorage.removeItem('token');  
  74.             window.location.href='/login';  
  75.         },  
  76.           
  77.     },  
  78.     
  79.     reducers: {  
  80.         updateLang (state,{payload:{value}}) {  
  81.             return state.set('i18n',value);  
  82.         },  
  83.         updateStore (state, { payload }) {  
  84.             return payload?state.mergeDeep(fromJS(payload)):initState  
  85.         },  
  86.           
  87.     },  
  88.     
  89.   };  
  90.     

以上修改做了两件事,1封装menuMap数据结构,2存储在app的model中menu对象,效果如下:


接下来,我们就可以在任何地方获取并使用它了

(1)在组件中使用,需要connect数据 menu:app.get('menu'),以routes/home/index.js为例,修改如下:

[html] view plain copy

  1. import React, {Component} from 'react';  
  2. import {connect} from 'dva'  
  3. import {Link} from 'dva/router'  
  4. import {injectIntl} from 'react-intl'  
  5. import {Row, Col, Form, Button} from 'antd'  
  6. import classnames from 'classnames';  
  7. import styles from './index.less';  
  8.   
  9. class Home extends Component{  
  10.   
  11.     render(){  
  12.         const {menu} = this.props;  
  13.   
  14.         const aaaUrl=menu.getIn(['byId','aaa','path']);  
  15.   
  16.         return(  
  17.             <Row>  
  18.                 <Col className={classnames(styles.home)}>  
  19.                     欢迎您,来到首页  
  20.                 </Col>  
  21.                 <Col>  
  22.                     <Link to={aaaUrl}><Button>去AAA页面</Button></Link>  
  23.                 </Col>  
  24.             </Row>  
  25.         )  
  26.     }  
  27. }  
  28.   
  29. export default connect(({  
  30.     app  
  31. })=>({  
  32.     menu:app.get('menu')  
  33. }))(injectIntl(Form.create()(Home)))  

(2)在models中使用,需要select数据 yield select(_=>_.app.getIn(['menu','byId','home','path'])), 以models/app.js为例,修改loginOk如下:

[html] view plain copy

  1. import {Map, fromJS} from 'immutable';  
  2. import {routerRedux} from 'dva/router';  
  3. import {config} from '../utils';  
  4. const {menuMap} = config;  
  5.   
  6. const initState = Map({  
  7.     i18n: 'zh_CN',  
  8.     token:null,  
  9.     locationPathname:null,  
  10.     menu:menuMap  
  11. })  
  12.   
  13. export default {  
  14.   
  15.     namespace: 'app',  
  16.     
  17.     state:initState,  
  18.     
  19.     subscriptions: {  
  20.         setup({ dispatch, history }) {  
  21.           
  22.         },  
  23.         setupHistory ({ dispatch, history }) {  
  24.             history.listen((location) => {  
  25.                 dispatch({  
  26.                     type: 'updateLocation',  
  27.                     payload: {  
  28.                       locationPathname: location.pathname  
  29.                     },  
  30.                 });  
  31.                 dispatch({  
  32.                     type: 'updateToken',  
  33.                     payload: {  
  34.                       token: window.sessionStorage.getItem('token')  
  35.                     },  
  36.                 })  
  37.             })  
  38.         },  
  39.     },  
  40.     
  41.     effects: {  
  42.   
  43.         * changeLang ({  
  44.             payload: {value},  
  45.         }, { put }) {  
  46.             yield put({ type: 'updateLang', payload: {value}});  
  47.         },  
  48.   
  49.         * updateLocation ({  
  50.             payload  
  51.         }, {put, select}) {  
  52.             yield put({type: 'updateStore', payload});  
  53.         },  
  54.   
  55.         * updateToken ({  
  56.             payload  
  57.         }, {put, select}) {  
  58.             yield put({type: 'updateStore', payload});  
  59.         },  
  60.   
  61.         * loginOk ({  
  62.             payload  
  63.         }, {put, select}) {  
  64.             const homeUrl = yield select(_=>_.app.getIn(['menu','byId','home','path']))  
  65.             window.sessionStorage.setItem('token',payload.token);  
  66.             yield put(routerRedux.push({  
  67.                 pathname: homeUrl  
  68.             }));  
  69.         },  
  70.   
  71.         * logout ({  
  72.             payload  
  73.         }, {put, select}) {  
  74.             window.sessionStorage.removeItem('token');  
  75.             window.location.href='/login';  
  76.         },  
  77.           
  78.     },  
  79.     
  80.     reducers: {  
  81.         updateLang (state,{payload:{value}}) {  
  82.             return state.set('i18n',value);  
  83.         },  
  84.         updateStore (state, { payload }) {  
  85.             return payload?state.mergeDeep(fromJS(payload)):initState  
  86.         },  
  87.           
  88.     },  
  89.     
  90.   };  
  91.     

至此,刷新下试试,一切正常吧!


下面给大家推荐一款路由正则匹配的工具:path-to-regexp,用来更简易的操作路径匹配问题,有兴趣的同学可以自行学习。

在首页增加一个路由,用作编辑和新增页面,修改utils/config.js如下:

[html] view plain copy

  1. import {OrderedSet,Map,fromJS} from 'immutable'  
  2.   
  3. const menuGlobal=[  
  4.     {  
  5.         id:'login',  
  6.         pid:'0',  
  7.         name:'登录',  
  8.         icon:'user',  
  9.         path: '/login',  
  10.         models: () => [import('../models/login')], //models可多个  
  11.         component: () => import('../routes/login'),  
  12.     },   
  13.     {  
  14.         id:'home',  
  15.         pid:'0',  
  16.         name:'首页',  
  17.         icon:'user',  
  18.         path: '/',  
  19.         models: () => [import('../models/home')], //models可多个  
  20.         component: () => import('../routes/home'),  
  21.     },   
  22.     {  
  23.         id:'home-edit',  
  24.         pid:'home',  
  25.         name:'首页-编辑和新增',  
  26.         icon:'user',  
  27.         path: '/edit/:id?',  
  28.         models: () => [import('../models/home')], //models可多个  
  29.         component: () => import('../routes/home/edit'),  
  30.     },   
  31.     {  
  32.         id:'aaa',  
  33.         pid:'0',  
  34.         name:'aaa页',  
  35.         icon:'user',  
  36.         path: '/aaa',  
  37.         models: () => [import('../models/aaa')], //models可多个  
  38.         component: () => import('../routes/AAA'),  
  39.     },   
  40.     {  
  41.         id:'bbb',  
  42.         pid:'0',  
  43.         name:'bbb页',  
  44.         icon:'user',  
  45.         path: '/aaa/bbb',  
  46.         models: () => [import('../models/bbb')], //models可多个  
  47.         component: () => import('../routes/BBB'),  
  48.     },   
  49.     {  
  50.         id:'ccc',  
  51.         pid:'0',  
  52.         name:'ccc页',  
  53.         icon:'user',  
  54.         path: '/ccc',  
  55.         models: () => [import('../models/ccc')], //models可多个  
  56.         component: () => import('../routes/CCC'),  
  57.     },   
  58. ];  
  59.   
  60. /**  
  61.  * 封装路由数据,利用id和pid的关联性处理  
  62.  */  
  63. const menuMap = (() => {  
  64.     let byId = Map();  
  65.     let byPid = Map();  
  66.     menuGlobal.map(item => {  
  67.       byId = byId.set(item.id, fromJS(item));  
  68.       byPid = byPid.update(item.pid, obj => obj ? obj.add(item.id) : OrderedSet([item.id]))  
  69.         
  70.     });  
  71.     return Map({  
  72.         byId,  
  73.         byPid  
  74.     });  
  75. })();  
  76.     
  77. export default {  
  78.     menuGlobal,  
  79.     menuMap  
  80. }  

对应增加组件routes/home/edit.js代码如下:

[html] view plain copy

  1. import React, {Component} from 'react';  
  2. import {connect} from 'dva'  
  3. import {Link} from 'dva/router'  
  4. import {injectIntl} from 'react-intl'  
  5. import {Row, Col, Form, Button} from 'antd'  
  6. import classnames from 'classnames';  
  7. import styles from './index.less';  
  8.   
  9. class Home extends Component{  
  10.   
  11.     goBack=()=>{  
  12.         const {dispatch} = this.props;  
  13.         dispatch({  
  14.             type:'app/goBack'  
  15.         })  
  16.     }  
  17.   
  18.     render(){  
  19.         const {menu,match} = this.props;  
  20.         const id=match.params.id;  
  21.   
  22.         return(  
  23.             <Row>  
  24.                 <Col className={classnames(styles.home)}>  
  25.                      欢迎您,来到home<span style={{fontSize:'24px'}}>{id?`编辑${id}`:`新增`}</span>页面  
  26.                 </Col>  
  27.                 <Col>  
  28.                     <Button onClick={this.goBack}>返回</Button>  
  29.                 </Col>  
  30.             </Row>  
  31.         )  
  32.     }  
  33. }  
  34.   
  35. export default connect(({  
  36.     app  
  37. })=>({  
  38.     menu:app.get('menu')  
  39. }))(injectIntl(Form.create()(Home)))  

这里用到了返回上一页,只需要在models/app.js中增加effects,用于返回上一页

[html] view plain copy

  1. * goBack ({  
  2.             payload  
  3.         }, {put, select}) {  
  4.             yield put(routerRedux.goBack());  
  5.         },  

修改routes/home/index.js,灵活使用pathToRegexp.compile(homeEditUrl)({id:1}) 如下:

[html] view plain copy

  1. import React, {Component} from 'react';  
  2. import {connect} from 'dva'  
  3. import {Link} from 'dva/router'  
  4. import {injectIntl} from 'react-intl'  
  5. import {Row, Col, Form, Button} from 'antd'  
  6. import classnames from 'classnames';  
  7. import pathToRegexp from 'path-to-regexp'  
  8. import styles from './index.less';  
  9.   
  10. class Home extends Component{  
  11.   
  12.     render(){  
  13.         const {menu} = this.props;  
  14.   
  15.         const aaaUrl=menu.getIn(['byId','aaa','path']);  
  16.         const homeEditUrl=menu.getIn(['byId','home-edit','path']);  
  17.   
  18.         return(  
  19.             <Row>  
  20.                 <Col className={classnames(styles.home)}>  
  21.                     欢迎您,来到首页  
  22.                 </Col>  
  23.                 <Col>  
  24.                     <Link to={aaaUrl}><Button>去AAA页面</Button></Link>  
  25.                     <Link to={pathToRegexp.compile(homeEditUrl)()}><Button>新增</Button></Link>  
  26.                     <Link to={pathToRegexp.compile(homeEditUrl)({id:1})}><Button>编辑(id=1)</Button></Link>  
  27.                     <Link to={pathToRegexp.compile(homeEditUrl)({id:2})}><Button>编辑(id=2)</Button></Link>  
  28.                     <Link to={pathToRegexp.compile(homeEditUrl)({id:123})}><Button>编辑(id=123)</Button></Link>  
  29.                 </Col>  
  30.             </Row>  
  31.         )  
  32.     }  
  33. }  
  34.   
  35. export default connect(({  
  36.     app  
  37. })=>({  
  38.     menu:app.get('menu')  
  39. }))(injectIntl(Form.create()(Home)))  

然后修改models/home.js,灵活使用pathToRegexp(homeUrl).exec(pathname) 如下:

[html] view plain copy

  1. import pathToRegexp from 'path-to-regexp'  
  2.   
  3. export default {  
  4.   
  5.     namespace: 'home',  
  6.     
  7.     state: {  
  8.       name:'这是home的model'  
  9.     },  
  10.     
  11.     subscriptions: {  
  12.       setup({ dispatch, history }) {  
  13.         return history.listen(({ pathname, query }) => {  
  14.           dispatch({type: 'dataInit', payload: {pathname}});  
  15.         });  
  16.       },  
  17.     },  
  18.     
  19.     effects: {  
  20.       * dataInit({payload: {pathname}}, {put,call,select}){  
  21.   
  22.         const homeUrl = yield select(_=>_.app.getIn(['menu','byId','home','path']));  
  23.         const homeEditUrl = yield select(_=>_.app.getIn(['menu','byId','home-edit','path']));  
  24.           
  25.         if(pathToRegexp(homeUrl).exec(pathname)){  
  26.           console.log('home页面执行')  
  27.             
  28.         }else if(pathToRegexp(homeEditUrl.substring(0,homeEditUrl.lastIndexOf('?')-4)).exec(pathname)){  
  29.           console.log('home-新增页面执行')  
  30.             
  31.         }else if(pathToRegexp(homeEditUrl.substring(0,homeEditUrl.lastIndexOf('?'))).exec(pathname)){  
  32.           console.log('home-编辑页面执行')  
  33.             
  34.         }  
  35.   
  36.       },  
  37.     },  
  38.     
  39.     reducers: {  
  40.         
  41.     },  
  42.     
  43.   };  
  44.     

好了,我们的预期是:

(1)访问https://localhost:9999/,控制台输出“home页面执行”

(2)访问https://localhost:9999/edit,控制台输出“home-新增页面执行”

(3)访问https://localhost:9999/edit/1,控制台输出“home-编辑页面执行”

下面看看效果:



哦了,就是这个样子!

再给大家介绍一个常用的工具react-helmet,什么东西呢,直观翻译‘头盔’,用在react组件中,就是用来处理页面的head部分构成的,有兴趣的同学自行学习。安装:cnpm i react-helmet --save

先来看看现在的页面title:


增加/layout/layout.js文件,代码:

[html] view plain copy

  1. import {connect} from 'dva';  
  2. import React from 'react';  
  3. import pathToRegexp from 'path-to-regexp'  
  4. import Helmet from 'react-helmet';  
  5.   
  6. const Layout=({ children,dispatch,menu,locationPathname })=>{  
  7.   
  8.   const menuList=menu.getIn(['byId']).toList();  
  9.   let menuName='';  
  10.   menuList.map(item=>{  
  11.     if(pathToRegexp(item.get('path')).exec(locationPathname)){  
  12.       menuName = item.get('name');  
  13.     }  
  14.   });  
  15.   
  16.   return (  
  17.     <React.Fragment>  
  18.       <Helmet>  
  19.         <title>  
  20.           {menuName}  
  21.         </title>  
  22.       </Helmet>  
  23.       {children}  
  24.     </React.Fragment>  
  25.   );  
  26. }  
  27.   
  28. export default connect(({  
  29.   app  
  30. })=>({  
  31.   menu:app.get('menu'),  
  32.   locationPathname:app.get('locationPathname'),  
  33. }))(Layout)  

修改/layout/auth.js 如下:

[html] view plain copy

  1. import {connect} from 'dva';  
  2. import React from 'react';  
  3. import Layout from './layout';  
  4.   
  5. const Auth=({ children,dispatch,token,locationPathname })=>{  
  6.   
  7.   if(!token&&locationPathname!='/login'){  
  8.     dispatch({  
  9.       type:'app/logout'  
  10.     })  
  11.   }else if(token&&locationPathname=='/login'){  
  12.     dispatch({  
  13.       type:'app/loginOk',  
  14.       payload:{  
  15.         token:token  
  16.       }  
  17.     })  
  18.   }  
  19.   
  20.   return (  
  21.     <Layout>  
  22.       {children}  
  23.     </Layout>  
  24.   );  
  25. }  
  26.   
  27. export default connect(({  
  28.   app  
  29. })=>({  
  30.   token:app.get('token'),  
  31.   locationPathname:app.get('locationPathname'),  
  32. }))(Auth)  

OK,我们再来看页面title:




此时,title显示的已经是menuGlobal中配置的name值了。

以上是 react项目开发-路由优化(前三篇续) 的全部内容, 来源链接: utcz.com/z/383657.html

回到顶部