vue动态路由,前端请求路由。addRoutes。
需求:
之前都是前端固定写好路由,然后跳来跳去,虽然做的有根据登录者类型去做左侧菜单筛选(隐藏,显示),也添加了路由守卫对路由进行拦截处理。但是并不安全,因为在初次加载时就默认已经加载了全部路由信息,而且登陆角色类型是存储在缓存中的,稍微修改就会导致菜单重新刷新获取,这样路由、权限也就乱了。具体做法请看 静态路由,分角色进行权限控制。综合考虑后,决定采用动态获取路由的方式去做。
代码 :
router文件
首先,router/index中引入必须的路由。比如登录,像登陆这种是不需要放在后台进行获取的。其中Home路由是我左右布局的路由,这个就固定写在上面了。login登录需要,也是放在固定的路由中。
import Vue from 'vue'
import Router from 'vue-router'
const Home = () => import('@/components/Home') //主菜单
Vue.use(Router)
export default new Router({
routes: [
//此路由位置,谁排在前面先加载谁
{
path: '/',
redirect: '/login',
hidden:true,
userTypeArry:['1','2','3']
},
{
path: '/login',
name: 'login',
leaf: true,
component: resolve =>require(['@/components/Login'],resolve),
userTypeArry:['1','2','3'],
hidden:true
},
]
});
模拟请求路由
去模拟一个请求,其中post这是我封装的一个axios。其实就是返回来了一个对象数组,至于res.data.data.permissions[3]这个是我的模拟数据,模拟取一条出来。里面的child里面的path就是别名,component就是后台返回的组件路由url,name就是你给的名字,其中conponent中的路由信息不能全部放在后台,比如这样 ‘@/components/view/braceletManage/braceletList’,这样是加载不成功的,会报错‘ the request of a dependency is an expression’,所以要采用拼接的写法。
import axios from 'axios';
import {get,post} from './request' //封装axios
function getMenuList(objdatas){
let data = post('/eBackUser/login',objdatas).then(function(res){
console.log(res);
let menulist = null;
if(res.data){
let routerInfo = res.data.data.permissions[3];
//routerInfo.childPermission[0].url;
menulist= [{
path: '',
name: routerInfo.name,
component: resolve =>require(['@/components/Home'],resolve),
userTypeArry:['1','2','3'],
iconCls: 'iconmenu iconfont icon-zhinengshouhuani',
children: [
{ path: routerInfo.childPermission[0].permission, component: resolve =>require(['@/components/view/'+routerInfo.childPermission[0].url],resolve), name: routerInfo.childPermission[0].name }
]
}
];
}
return menulist;
})
return data;
};
export default getMenuList;
登录
点击登录去调用获取动态路由的函数(getMenuList),注意获取路由时,因为要发请求,所以要搞成同步的,要使用async和awit,不然添加路由会出错。outer.options.routes.push放的是一个个对象,router.addRoutes放的是一个数组对象,添加路由后,我用一个 “l” 把添加后的路由长度进行了记录存储,在刷新页面路由丢失时用到。这个时候不出意外应该是添加成功了,可以登录进去看到正常的菜单了。
methods: {
login() {
let that = this;
that.loginicon = "el-icon-loading";
if(that.user_name != "" && that.password!= ""){
let obj={
user_name:'admin_ahjd',
password:'5a673451b6974da765d58f9845ebac2c'
}
that.baseFun.setSessionStorage("uandp",JSON.stringify(obj));
that.$store.commit("setUandp",that.baseFun.getSessionStorage("uandp"));
getInfo(obj);
}
async function getInfo(obj){
let infos = await getMenuList(obj);
if(infos!=null){
// return false;
router.options.routes.push(infos[0]);
router.addRoutes(infos);
window.sessionStorage.setItem("l",router.options.routes.length);
that.$router.push("/UserList");
}else{
that.$message({
type: 'error',
message: '接口异常!'
});
return false;
}
}
}
},
解析菜单
解析菜单没什么好说的,就不写了。
刷新页面菜单丢失!
路由添加成功后,点击正常,但是刷新页面后addRoutes添加的路由就全部没了,这个时候需要在main.js中就行逻辑处理,main.js中的代码,每次页面在刷新时都会重新执行一遍,所以要在main.js中重新去获取一遍路由,注意:main.js中的代码初次进入就会执行一次,为了防止初次进入就执行跟登陆时执行 导致添加两次路由的情况出现,需要进行判断,在初次进入时判断 “l” 有无值,初次进入时 “l”肯定没有值,所以初次进入时是不会执行获取请求的操作,而有值的情况下(有值的情况就是你登录之后的情况),这个时候因为你刚登陆。“l”的长度肯定是和路由长度相等的,这个时候也不会进行重复请求路由操作。但是如果你登陆后做刷新页面操作,这个时候路由的长度就变了,就和"l"不相等了,这个时候就会重新去请求路由菜单。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import 'babel-polyfill'
import Vue from 'vue'
import App from './App'
import baseFun from './baseFun'// 引入公共方法函数
import {get,post} from './request' //封装axios
Vue.prototype.baseFun = baseFun
Vue.prototype.server = {get,post}
import router from './router' //路由
import ElementUI from 'element-ui'; //饿了么UI
import 'element-ui/lib/theme-chalk/index.css'; //饿了么UI
import store from './store'
import Viewer from 'v-viewer'//安装点击放大视图依赖
import 'viewerjs/dist/viewer.css'//放大视图
import getMenuList from './getMenu'
Vue.use(Viewer)
Vue.config.productionTip = false
Vue.use(ElementUI); //饿了么UI
//路由跳转后返回顶部
router.afterEach((to,from,next) => {
window.scrollTo(0,0);
});
async function getMenu(){
let upObj = JSON.parse(baseFun.getSessionStorage("uandp"));
let infos = await getMenuList(upObj);
router.options.routes.push(infos[0]);
router.addRoutes(infos);
}
if(baseFun.getSessionStorage("l") && typeof(baseFun.getSessionStorage("l")) != "undefined"){
if(baseFun.getSessionStorage("l") != router.options.routes.length){
console.log(router);
console.log("!===");
getMenu();
}
}
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
退出系统,清空添加的路由。
登陆后添加成功路由后,这个时候如果点击退出,退到登陆页面。这个时候需要清空路由,不然这个时候再点击登录,就会重复添加。刷新当前页面就可以清空。window.locaton.reload();但是这个刷新操作放在什么地方呢,起初我是放在退出的vue文件里面,但是不行,执行刷新操作后后面的代码就会阻断,就不会跳往登录了。如果放在登录组件中也不行,会导致死循环刷新,后来我想到通过传参的方式解决。传参有两种方式 一种是push一种是params。通过push传递的参数在刷新页面后会丢失。所以我就用这种方式来做。在login的生命周期函数中通过判断有无参数进行刷新操作。另外退出时要删除 "l"
退出
signOut() {
this.$confirm('确定要退出吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: "messageBox-customClass"
}).then(() => {
window.sessionStorage.removeItem("l"); //退出时删除l
this.$router.push({
name:"login",
params:{
from:'goout'
}
}) ;
}).catch(() => {
});
},
login组件内
mounted() {
if(this.$route.params.from == "goout"){ //如果地址栏有from参数则进行刷新当前页面。
console.log("执行刷新,清空路由!");
window.location.reload();
}
}
做的时候借鉴了这位大神的文章-----------链接,感谢她的分享。