使用场景:用户登录后获取菜单列表,然后动态添加路由和菜单,实现根据用户权限实现动态添加路由。

昨天搞了半个晚上一直困惑使用(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

思考问题:如何动态添加路由菜单,就是有一个添加菜单功能,每添加一个导航菜单就动态添加一个路由?