最近一直在忙着一个用vue来作的权限管理的项目,其实在此以前,我也研究过vue的权限如何实现,而且也为之写过一篇博客,但当真正应用在项目中的时候,仍是发现了许多问题,因此此篇也会就着我在项目中遇到的一些问题,拿出来和你们分享一下,固然示例代码仍是个人github仓库中的ant-design-vue-ms。vue
对于一个先后端分离的项目而言,权限再也不是仅仅靠后端来控制,后端只能控制接口的权限,前台的页面显示仍是须要咱们来控制,针对vue的项目,首先我想的是当权限很少,而且都是单个权限的状况下,咱们彻底没有必要使用vue中提供的addRoutes的方法,可使用动态组件来作,即咱们根据后端返回的角色,来细度控制动态组件的显示内容,所谓动态组件其实就是vue内置提供的component组件webpack
<component :is="currentComponent"/>
复制代码
相信看到这里,熟悉的同窗应该已经想起来了,这样的话,咱们就不须要用到vuex,以及路由配置等等复杂的问题,单纯靠后台返回的角色名称就能解决全部的问题了,看到这里是否是以为今天的内容就这些了,别着急,下面还有“好看的”。git
这样虽然能解决一些简单权限的问题,可是针对稍微复杂一些的权限应用,就显得有些力不从心了,当角色过多,而且还包含了混合角色的权限的话,则会衍生出不少问题,这里也是列举我遇到的一些问题,同窗们能够细细推敲一下。github
因此综上所述,最终我仍是选择了传统的addRoutes,那么确定会有同窗问了,既然这个方案不行,那干吗还要用呢。问得好,其实动态组件就是一种尝试,只有知道错了,不知足需求了,咱们才能更知道为何会去使用传统的addRoutes的权限方案。web
因此咱们来看看addRoutes带来的一些“好处”:vuex
相信作过权限的同窗对上面的内容仍是有一些心得的,而后咱们按照该有的步骤一步一步来,这些步骤在上面个人github中已经有了,你们能够对照一下。后端
if (store.getters.roles.length === 0) {
store
.dispatch('GetInfo')
.then(res => {
const roles = res.data.resultData && res.data.resultData.roles
store.dispatch('GenerateRoutes', { roles }).then(() => {
// 根据roles权限生成可访问的路由表
// 动态添加可访问路由表
router.addRoutes(store.getters.addRouters)
})
})
.catch(() => {
store.dispatch('Logout').then(() => {
next({ path: '/user/login', query: { redirect: to.fullPath } })
})
})
} else {
next()
}
复制代码
这里代码作了简化,主要给你们看下上面会有一个角色判断长度,主要是当咱们不刷新的状况,页面角色信息不回丢失,所以咱们也就没有必要去请求后台获取角色信息了,来节省请求数量。 2. 经过上面的代码能够看到,咱们首先是请求的角色信息,而后请求了生成路由的GenerateRoutes的方法,方法是写在vuex中的action里面的,这部分的内容由于网上有不少教程,其实主要概括一下,就是对路由进行递归过滤,过滤出符合角色的路由,而后将静态路由和过滤出来的动态路由连接起来bash
const permission = {
state: {
routers: constRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes({ commit }, data) {
//略
}
}
}
复制代码
export const constRouterMap = [
{
path: '/',
redirect: '/index',
component: BasicLayout,
children: [
{
path: '/index',
name: 'index',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
meta: {
title: '仪表盘'
}
},
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'),
meta: {
title: '表单页'
}
},
{
path: '/pattern',
name: 'pattern',
component: () => import(/* webpackChunkName: "pattern" */ '@/views/DesignPattern.vue')
},
{
path: '/map',
name: 'map',
component: () => import(/* webpackChunkName: "map" */ '@/views/DataMap.vue'),
meta: {
title: '地图组件'
}
},
]
},
{
path: '/user',
redirect: '/login',
component: UserLayout,
children: [
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ '@/views/user/Login.vue')
},
{
path: '/register',
name: 'register',
component: () => import(/* webpackChunkName: "login" */ '@/views/user/Register.vue')
}
]
},
//须要注意这里,404的路由必定要写在静态路由中
{
path: '/404',
component: () => import(/* webpackChunkName: "not_found" */ '@/views/NotFound.vue')
}
]
export const asyncRouterMap = [
{
path: '/',
redirect: '/index',
component: BasicLayout,
children: [
{
path: '/controls',
name: 'controls',
component: () => import(/* webpackChunkName: "controls" */ '@/views/Controls.vue'),
meta: {
title: '权限设置',
permission: ['admin']
}
}
]
},
//捕获未定义的路由配置
{
path: '*',
redirect: '/404',
hidden: true
}
]
复制代码
上面关于404页面的定义顺序很是重要,若是在静态路由中定义了捕获的路由path:"*",而在动态路由中定义了404路由的话,则当导航钩子中判断比较复杂的话,会出现一些意想不到的错误,我就是当时写反了顺序,而且还在导航钩子中作了一些复杂的面包屑的判断,一旦刷新页面的话,则会出现如下错误前后端分离
这种错误的产生,多是由于刷新时,导航钩子发现动态添加进来的路由找不到一直进行获取动态路由的方法,致使最后调用栈溢出所致使,所以你们在使用的时候必定要很是当心。 4. 当咱们生成路由后,退出应用的切换新的角色帐号进行登陆时,必定要记得的两件事,第一就是清空vuex里面的角色信息,在不刷新的状况下,这些信息是不会丢失的,当不一样角色的帐号登陆时,原来的角色依然存在,那么确定会出现问题,其次则是在跳转会登陆页的时候,须要设置刷新页面的代码window.location.reload();
this.$router.push({name: 'login'});
复制代码
先刷新之后再跳转到登陆页,这个则是由于addRoutes生成的路由在不刷新的状况下会一直存在,即便下个不一样角色的帐号登陆时,依然会拿以前存在的路由信息去进行过滤,这样过滤的结果必然是当前角色的路由一个都不存在,所以生成的路由信息仍是上个角色的路由,因此在完成了以前这些步骤时,必定不要忘记了作这两步,这样也才是一个完整的权限解决方案async
以上也是我在项目中一些收货吧,拿出来和你们分享,也是但愿你们少走一些弯路,留心咱们开发中遇到的每一个看似很小的问题,其实每每是咱们最后解决问题的关键,不管是从动态组件仍是动态路由,问题的出现也是咱们不断去完善本身的过程。