本系列文章并不是手把手的教程,主要介绍了核心思路并讲解了核心代码,完整的代码小伙伴们可以在github上star并clone下来研究。另外,原本计划把项目跑起来放到网上供小伙伴们查看,但是之前买服务器为了省钱,内存只有512m,两个应用跑不起来(已经有一个v部落开源项目在运行),因此小伙伴们只能将就看一下下面的截图了,github上有部署教程,部署到本地也可以查看完整效果。
项目地址:https://github.com/lenve/vhr
前面几篇文章,我们已经基本解决了服务端的问题,并封装了前端请求,本文我们主要来聊聊登录以及组件的动态加载。
本文是本系列的第五篇,建议先阅读前面的文章有助于更好的理解本文:
1.springboot+vue前后端分离,使用springsecurity完美处理权限问题(一)
2.springboot+vue前后端分离,使用springsecurity完美处理权限问题(二)
3.springsecurity中密码加盐与springboot中异常统一处理
4.axios请求封装和异常统一处理
登录状态保存当用户登录成功之后,需要将当前用户的登录信息保存在本地,方便后面使用。具体实现如下:
登录成功保存数据在登录操作执行成功之后,通过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});
}
});
storestore的核心代码如下:
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,如此之后,不同用户登录成功之后就可以看到不同的菜单了。
相关推荐:
vue组件之alert详解
jquery加载单文件vue组件方法分享
实例详解vue组件父子间通信之聊天室
以上就是权限管理模块中动态加载vue组件实例详解的详细内容。