使用场景:用户登录后获取菜单列表,然后动态添加路由和菜单,实现根据用户权限实现动态添加路由。
昨天搞了半个晚上一直困惑使用(Router.addRoutes)在路由拦截器里,一直问题,今天早上到了公司,咨询了下老大和一个前段大佬终于搞定了,直接上代码了
login.vue(登录界面)
import { resetRouter } from "@/router"
async login() {
resetRouter() // 调用router里的resetRouter方法重新匹配路由
this.$router.replace("/") // 重定向到首页
},
router.js(路由index.js)
/* 路由设置 */
import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store'
Vue.use(Router)
/* 动态载入路由模块, 调用远程api获取 */
const createRouter = () => {
let childrens = [] // 左侧菜单需要展示的数据
if (localStorage.getItem('user')) {
// 获取后台菜单数据
let res = 动态数据接口() // 核心代码 这个接口一定要使用同步调用,可以使用XMLHttpRequest 调用
filterRouter(res) // 递归整理成自己先要的数据
childrens = [...childrens, ...res] // 将整理好的数据拼接到 childrens 里
store.dispatch("generateRoutes", childrens) // 将动态菜单节点添加到缓存
}
const routes = [
{
name: '登录系统',
path: '/login',
component: () => import('@/pages/login')
},
{
name: 'error',
path: '/error',
component: () => import('@/components/error.vue'),
},
{
name: '主页',
path: '/',
component: () => import('@/pages/home'),
redirect:'/control', // 重定向
meta: {
icon: 'el-icon-monitor'
},
children: childrens
}
];
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err);
};
const router = new Router({ // 创建router实例
routes,
mode: "history" // 移除地址栏里边的#号键
});
// 路由拦截
router.beforeEach((to, from, next) => {
if (to.path === "/login") {
next();
} else {
let token = store.getters.token
if (token) {
next();
} else {
// 没有登录的时候跳转到登录界面
next({ path: '/login' })
}
}
});
return router;
};
const router = createRouter();
// 重新匹配路由方法
export function resetRouter() {
const newRouter = createRouter();
router.matcher = newRouter.matcher; // 核心代码
}
/**
* 遍历后台传来的路由字符串,转换为组件对象
* @param {*} routerMap
*/
function filterRouter(routerMap) {
const accessedRouters = routerMap.filter(route => {
route.component = (resolve) => require(['@/pages' + route.path + ''], resolve)
if (route.children && route.children.length) {
route.children = filterRouter(route.children)
}
return true
})
return accessedRouters
}
export default router;
思路:
1.当初次加载时,根据token去判断是否去调用获取菜单路由接口,因为没有登录,所以router里只有基本的登录、异常路由,不会加载动态路由;
2.当登录成功后执行router里的resetRouter方法,这里的token就有值了,就可以调用获取菜单路由接口,将获取了的数据存到缓存里(为动态添加左侧菜单使用),然后整理后拼接到router里然里,然后使用matcher重新匹配路由,实现动态添加路由功能;
3.在home.vue(使用的是element-ui的 Container 布局容器)里通过缓存里的值,动态绑定左侧菜单,然后展示出来,具体显示成什么样子,可以根据自己需求定;
4.这里为什么要使用缓存,不使用 this.$router.options 取动态绑定左侧菜单呢。因为这里如果使用这个有问题,再初次显示的时候 this.$router.options 里没有动态的路由,可能和 router.matcher 的重新匹配机制有关系,具体不知道哪里的问题,等我做过调研了在贴出来原因,如果有知道的小伙伴一定要告诉我呀!一定要告诉我呀!一定要告诉我呀!重要的事情要说三遍嘛!
Container 布局容器:“https://element.faas.ele.me/#/zh-CN/component/container”
思考问题:如何动态添加路由菜单,就是有一个添加菜单功能,每添加一个导航菜单就动态添加一个路由?