动态加载权限管理模块中的Vue组件


Posted in Javascript onJanuary 16, 2018

本文我们主要来聊聊登录以及组件的动态加载。

登录状态保存

当用户登录成功之后,需要将当前用户的登录信息保存在本地,方便后面使用。具体实现如下:

登录成功保存数据

在登录操作执行成功之后,通过commit操作将数据提交到store中,核心代码如下:

this.postRequest('/login', {
  username: this.loginForm.username,
  password: this.loginForm.password
}).then(resp=> {
  if (resp && resp.status == 200) {
  var data = resp.data;
  _this.$store.commit('login', data.msg);
  var path = _this.$route.query.redirect;
  _this.$router.replace({path: path == '/' || path == undefined ? '/home' : path});
  }
});

store

store的核心代码如下:

export default new Vuex.Store({
 state: {
  user: {
   name: window.localStorage.getItem('user' || '[]') == null ? '未登录' : JSON.parse(window.localStorage.getItem('user' || '[]')).name,
   userface: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).userface
  }
 },
 mutations: {
  login(state, user){
   state.user = user;
   window.localStorage.setItem('user', JSON.stringify(user));
  },
  logout(state){
   window.localStorage.removeItem('user');
  }
 }
});

为了减少麻烦,用户登录成功后的数据将被保存在localStorage中(防止用户按F5刷新之后数据丢失),以字符串的形式存入,取的时候再转为json。当用户注销登陆时,将localStorage中的数据清除。

组件动态加载

在权限管理模块中,这算是前端的核心了。

核心思路

用户在登录成功之后,进入home主页之前,向服务端发送请求,要求获取当前的菜单信息和组件信息,服务端根据当前用户所具备的角色,以及角色所对应的资源,返回一个json字符串,格式如下:

[
  {
    "id": 2,
    "path": "/home",
    "component": "Home",
    "name": "员工资料",
    "iconCls": "fa fa-user-circle-o",
    "children": [
      {
        "id": null,
        "path": "/emp/basic",
        "component": "EmpBasic",
        "name": "基本资料",
        "iconCls": null,
        "children": [],
        "meta": {
          "keepAlive": false,
          "requireAuth": true
        }
      },
      {
        "id": null,
        "path": "/emp/adv",
        "component": "EmpAdv",
        "name": "高级资料",
        "iconCls": null,
        "children": [],
        "meta": {
          "keepAlive": false,
          "requireAuth": true
        }
      }
    ],
    "meta": {
      "keepAlive": false,
      "requireAuth": true
    }
  }
]

前端在拿到这个字符串之后,做两件事:1.将json动态添加到当前路由中;2.将数据保存到store中,然后各页面根据store中的数据来渲染菜单。

核心思路并不难,下面我们来看看实现步骤。

数据请求时机

这个很重要。

可能会有小伙伴说这有何难,登录成功之后请求不就可以了吗?是的,登录成功之后,请求菜单资源是可以的,请求到之后,我们将之保存在store中,以便下一次使用,但是这样又会有另外一个问题,假如用户登录成功之后,点击某一个子页面,进入到子页面中,然后按了一下F5进行刷新,这个时候就GG了,因为F5刷新之后store中的数据就没了,而我们又只在登录成功的时候请求了一次菜单资源,要解决这个问题,有两种思路:1.将菜单资源不要保存到store中,而是保存到localStorage中,这样即使F5刷新之后数据还在;2.直接在每一个页面的mounted方法中,都去加载一次菜单资源。

由于菜单资源是非常敏感的,因此最好不要不要将其保存到本地,故舍弃方案1,但是方案2的工作量有点大,因此我采取办法将之简化,采取的办法就是使用路由中的导航守卫。

路由导航守卫

我的具体实现是这样的,首先在store中创建一个routes数组,这是一个空数组,然后开启路由全局守卫,如下:

router.beforeEach((to, from, next)=> {
  if (to.name == 'Login') {
   next();
   return;
  }
  var name = store.state.user.name;
  if (name == '未登录') {
   if (to.meta.requireAuth || to.name == null) {
    next({path: '/', query: {redirect: to.path}})
   } else {
    next();
   }
  } else {
   initMenu(router, store);
   next();
  }
 }
)

这里的代码很短,我来做一个简单的解释:

1.如果要去的页面是登录页面,这个没啥好说的,直接过。

2.如果不是登录页面的话,我先从store中获取当前的登录状态,如果未登录,则通过路由中meta属性的requireAuth属性判断要去的页面是否需要登录,如果需要登录,则跳回登录页面,同时将要去的页面的path作为参数传给登录页面,以便在登录成功之后跳转到目标页面,如果不需要登录,则直接过(事实上,本项目中只有Login页面不需要登录);如果已经登录了,则先初始化菜单,再跳转。

初始化菜单的操作如下:

export const initMenu = (router, store)=> {
 if (store.state.routes.length > 0) {
  return;
 }
 getRequest("/config/sysmenu").then(resp=> {
  if (resp && resp.status == 200) {
   var fmtRoutes = formatRoutes(resp.data);
   router.addRoutes(fmtRoutes);
   store.commit('initMenu', fmtRoutes);
  }
 })
}
export const formatRoutes = (routes)=> {
 let fmRoutes = [];
 routes.forEach(router=> {
  let {
   path,
   component,
   name,
   meta,
   iconCls,
   children
  } = router;
  if (children && children instanceof Array) {
   children = formatRoutes(children);
  }
  let fmRouter = {
   path: path,
   component(resolve){
    if (component.startsWith("Home")) {
     require(['../components/' + component + '.vue'], resolve)
    } else if (component.startsWith("Emp")) {
     require(['../components/emp/' + component + '.vue'], resolve)
    } else if (component.startsWith("Per")) {
     require(['../components/personnel/' + component + '.vue'], resolve)
    } else if (component.startsWith("Sal")) {
     require(['../components/salary/' + component + '.vue'], resolve)
    } else if (component.startsWith("Sta")) {
     require(['../components/statistics/' + component + '.vue'], resolve)
    } else if (component.startsWith("Sys")) {
     require(['../components/system/' + component + '.vue'], resolve)
    }
   },
   name: name,
   iconCls: iconCls,
   meta: meta,
   children: children
  };
  fmRoutes.push(fmRouter);
 })
 return fmRoutes;
}

在初始化菜单中,首先判断store中的数据是否存在,如果存在,说明这次跳转是正常的跳转,而不是用户按F5或者直接在地址栏输入某个地址进入的。否则就去加载菜单。拿到菜单之后,首先通过formatRoutes方法将服务器返回的json转为router需要的格式,这里主要是转component,因为服务端返回的component是一个字符串,而router中需要的却是一个组件,因此我们在formatRoutes方法中动态的加载需要的组件即可。数据格式准备成功之后,一方面将数据存到store中,另一方面利用路由中的addRoutes方法将之动态添加到路由中。

菜单渲染

最后,在Home页中,从store中获取菜单json,渲染成菜单即可,相关代码可以在Home.vue中查看,不赘述。

OK,如此之后,不同用户登录成功之后就可以看到不同的菜单了。

Javascript 相关文章推荐
js 调整select 位置的函数
Feb 21 Javascript
基于jquery的气泡提示效果
May 31 Javascript
js过滤HTML标签以及空格的思路及代码
May 24 Javascript
javascript:void(0)使用探讨
Aug 27 Javascript
JS DOM实现鼠标滑动图片效果
Sep 17 Javascript
Kendo Grid editing 自定义验证报错提示的解决方法
Nov 18 Javascript
微信小程序url与token设置详解
Sep 26 Javascript
BACKBONE.JS 简单入门范例
Oct 17 Javascript
JavaScript中字符串的常用操作方法及特殊字符
Mar 18 Javascript
解决vue路由后界面没有变化,但是链接有的问题
Sep 01 Javascript
IE11下CKEditor在Bootstrap Modal中下拉问题的解决
Sep 25 Javascript
详解基于element的区间选择组件校验(交易金额)
Jan 07 Javascript
vue2.0 兄弟组件(平级)通讯的实现代码
Jan 15 #Javascript
解析Angular 2+ 样式绑定方式
Jan 15 #Javascript
基于jquery的on和click的区别详解
Jan 15 #jQuery
仿淘宝JSsearch搜索下拉深度用法
Jan 15 #Javascript
vue2.0 父组件给子组件传递数据的方法
Jan 15 #Javascript
深入理解requireJS-实现一个简单的模块加载器
Jan 15 #Javascript
vue2.0 如何把子组件的数据传给父组件(推荐)
Jan 15 #Javascript
You might like
PHP一些有意思的小区别
2006/12/06 PHP
ASP和PHP都是可以删除自身的
2007/04/09 PHP
php读取txt文件组成SQL并插入数据库的代码(原创自Zjmainstay)
2012/07/31 PHP
phpmyadmin显示utf8_general_ci中文乱码的问题终级篇
2013/04/08 PHP
使用URL传输SESSION信息
2015/07/14 PHP
XAMPP升级PHP版本实现步骤解析
2020/09/04 PHP
JavaScript传递变量: 值传递?引用传递?
2011/02/22 Javascript
JavaScript中函数声明优先于变量声明的实例分析
2012/03/01 Javascript
解析DHTML,JavaScript,DOM,BOM以及WEB标准的描述
2013/06/19 Javascript
将Datatable转化成json发送前台实现思路
2013/09/06 Javascript
浅谈javascript的Array.prototype.slice.call
2015/08/31 Javascript
JavaScript代码轻松实现网页内容禁止复制(代码简单)
2015/10/23 Javascript
jQuery代码实现对话框右上角菜单带关闭×
2016/05/03 Javascript
vue表单绑定实现多选框和下拉列表的实例
2017/08/12 Javascript
js实现网页的两个input标签内的数值加减(示例代码)
2017/08/15 Javascript
jQuery实现点击自身以外区域关闭弹出层功能完整示例【改进版】
2018/07/31 jQuery
解析JS在获取当前月的最后一天遇到的坑
2019/08/30 Javascript
Layui Form 自定义验证的实例代码
2019/09/14 Javascript
ES6 Symbol在对象中的作用实例分析
2020/06/06 Javascript
比较详细Python正则表达式操作指南(re使用)
2008/09/06 Python
在Django框架中伪造捕捉到的URLconf值的方法
2015/07/18 Python
python3制作捧腹网段子页爬虫
2017/02/12 Python
初探TensorFLow从文件读取图片的四种方式
2018/02/06 Python
python爬虫之模拟登陆csdn的实例代码
2018/05/18 Python
对pycharm代码整体左移和右移缩进快捷键的介绍
2018/07/16 Python
详解python中TCP协议中的粘包问题
2019/03/22 Python
python针对Oracle常见查询操作实例分析
2020/04/30 Python
pandas按照列的值排序(某一列或者多列)
2020/12/13 Python
Python 获取异常(Exception)信息的几种方法
2020/12/29 Python
html5 canvas-2.用canvas制作一个猜字母的小游戏
2013/01/07 HTML / CSS
使用HTML和CSS实现的标签云效果(附demo)
2021/02/03 HTML / CSS
Joie官方网上商店:购买服装和女装配饰
2018/06/05 全球购物
The North Face官方旗舰店:美国著名户外品牌
2020/09/28 全球购物
粗加工管理制度
2014/02/04 职场文书
中学教师暑期培训方案
2014/08/27 职场文书
MySQL基础(一)
2021/04/05 MySQL