vue编写动态路由菜单

1,070 阅读4分钟

关于router动态添加路由和菜单所遇到的坑和解决方法,记录以防止下次遇到

学习和借鉴了一下:花裤衩大佬github的编写风格,有想学习的可以去瞻仰一下大佬的风格

  1. 在store里面对获取的路由信息进行处理
    import layout from "@/views/layout";

    const menu = {
    state: {
        routers: [],
        addRoutes: [],
    },
    mutations: {
        //获取路由信息
        SET_ROUTERS: (state, routers) => {
            state.routers = routers
        },
        //获取拼装好的路由路径
        SET_ADD_ROUTERS: (state, addRoutes) => {
            state.addRoutes = addRoutes
        },
    },
    actions: {
        //获取路由信息
        getRouters({commit}, menuData) {
            commit('SET_ROUTERS', menuData.data)

            let menu = filterChildren(menuData.data)
            //添加路由
            commit('SET_ADD_ROUTERS', menu)
            //左侧菜单路由
            commit('SET_SIDEBAR_ROUTERS',menu)
        }
    }
}

    //拼接路由
    function filterChildren(router) {
        return router.filter((route) => {
        //判断是否含有下级路由
        if (route.children && route.children.length > 0) {
            route.component = layout
            route.children = filterChildren(route.children)
        } else {
            route.component = loadView(route.component)

        }
        return true
    })
    }

    // 拼接路径
    function loadView(view) {
        return () => import(`@/views/${view}`);
    }
    export default menu
  1. 创建permission.js在根目录下,在main.js里面引用

    import NProgress from "nprogress";
    import store from "./store";
    import router from "./router";
    
    const NoLoginList = ['/clear']
    import menuData from '@/assets/model/menuData.json'
    
    router.beforeEach(async (to, from, next) => {
         let token = store.state.user.token;
         if (token) {
             //有登录并在登录页,直接进入首页
             if (to.path === "/login") {
                 next("/");
             } else {
                 //非登录页直接放过
                 //模拟后台请求数据
                 await store
                     .dispatch('getRouters', menuData)
                 router.addRoutes(store.state.menu.addRoutes)
                 next()
             }
         } else {
             //未登录,如果是免登录页面直接进入
             if (NoLoginList.indexOf(to.path) !== -1) {
                 next();
             } else if (to.path === '/login') {
                 //多加一步判断是否是去登录,以免进入路由死循环里
                 next()
             } else {
                 //非登录页,也不是免登录页就跳转到登录页
                 next({path: "/login", query: {redirect: to.fullPath}});
             }
         }
    });
    
    
    • 错误信息
     [vue-router] router.addRoutes() is deprecated and
     has been removed in Vue Router 4. Use router.addRoute() instead.
    

    原因:addRoutes()已弃用,并且已在Vue路由器4中删除。 解决:改为addRoute()就可以了

  2. 记录报错内容

    1. 使用router.addRoute()
         router.beforeEach(async (to, from, next) => {
             //其他代码省略
             router.addRoute(store.state.menu.addRoutes)
             //其他代码省略
         });
    

    运行报错信息

    //错误:[vue路由器]“路径”在路由配置中是必需的
    Uncaught (in promise) Error: [vue-router] "path" is required in a route configuration.
    

    原因:添加一条新的路由规则记录作为现有路由的子路由。不能直接添加数据类型的路由信息,需要一条一条的添加 如果该路由规则有 name,并且已经存在一个与之相同的名字,则会覆盖它

    解决:一条一条添加就行

    1. 使用router.addRoute()
         router.beforeEach(async (to, from, next) => {
             //其他代码省略
             store.state.menu.addRoutes.forEach((item)=>{
                router.addRoute(item)
             })
             next()
             //其他代码省略
         });
    

    运行报错信息:页面出现空白

    在路由的前置守卫里面, 使用 addRoutes 钩子后, 直接调用 next() 如果当前页面的 路由 是通过 addRoutes 添加进去的 刷新页面就会出现 对应的路由组件不会渲染的情况, 也不会报错,页面会显示为空白

    详细原因未知: 大致理解为, 当进入路由的前置钩子 (router.beforEach) 的时候, 路由的结构是不会发生变化的, 至少本次跳转, 路由的结构是不会变化的 也就是相当于我们举报一次活动,明天能看,但我们今天就要看一样,所以也就不能使用了

    1. 使用next({...to, replace: true})解决页面空白问题
         router.beforeEach(async (to, from, next) => {
             //其他代码省略
             store.state.menu.addRoutes.forEach((item)=>{
                router.addRoute(item)
             })
             // 这一行就是解决空白问题的最终办法, 重新进当前路由
             next({...to, replace: true})
             //其他代码省略
         });
    

    运行报错信息:加入这行代码会造成页面陷入死循环,每次都去添加,重进当前路由, 解决方法:只使用一次:在初次加载时添加路由,其他页面路由跳转不在添加 4. 加入isAddRouter判断是否是初次加载动态路由

         let isAddRouter = false
         router.beforeEach(async (to, from, next) => {
             //其他代码省略
             //判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取
             if (!isAddRouter) {
                 
                 store.state.menu.addRoutes.forEach((item) => {
                     router.addRoute(item)
                 })
                 // 这一行就是解决空白问题的最终办法, 重新进当前路由
                 next({...to, replace: true})
                 
             } else {
                 next()
             }
             //其他代码省略
         });
    
  3. 最终permission.js代码

import NProgress from "nprogress";
import store from "./store";
import router from "./router";

const NoLoginList = ['/clearToken'] //免登录页面
import menuData from '@/assets/model/menuData.json'

let isAddRouter = false

router.beforeEach(async (to, from, next) => {
    NProgress.start()
    let token = store.state.user.token;
    if (token) {
        //有登录并在登录页,直接进入首页
        if (to.path === "/login") {
            next("/");
        } else {
            //非登录页直接放过
            //判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取
            if (!isAddRouter) {
                //模拟后台请求数据
                await store
                    .dispatch('getRouters', menuData)
                //渲染之后改变状态,截止动态路由的再次渲染
                isAddRouter = true
                // router.options.routes里的数据需要手动添加,
                // router.addroutes/router.addroute添加的数据是不会自动到这里面的
                router.options.routes = [...store.state.menu.addRoutes]
                store.state.menu.addRoutes.forEach((item)=>{
                    router.addRoute(item)
                })

                // 这一行就是解决问题的最终办法, 重新进当前路由
                next({...to, replace: true})
            } else {
                next()
            }
        }
        NProgress.done()
    } else {
        //未登录,如果是免登录页面直接进入
        if (NoLoginList.indexOf(to.path) !== -1) {
            next();
        } else if (to.path === '/login') {
            //多加一步判断是否是去登录,以免进入路由死循环里
            next()
        } else {
            //非登录页,也不是免登录页就跳转到登录页
            next({path: "/login", query: {redirect: to.fullPath}});
        }
        NProgress.done()
    }
});