一:解决初次加载子菜单报错 No match found for location with path “xxx”

最近在做微型人事项目,进行到左边导航菜单动态加载部分,前端所有的子项都无法打开
在这里插入图片描述
以基本资料为例,点击会在控制台弹出如下警告信息:
在这里插入图片描述
教程中src/utils/menus.js内的initMenu方法从后台请求得到动态的菜单栏数据data,使用formatRoutes方法将data中的“component”对应的字符串数据转化为.vue组件。
教程代码:

import {getRequest} from "./api";

export const initMenu = (router, store) => {
    if (store.state.routes.length > 0) {
        return;
    }
    getRequest("/system/config/menu").then(data => {
        if (data) {
            var str1 = JSON.stringify(data)
            console.log("data:"+str1)
            let fmtRoutes = formatRoutes(data);
            console.log("fmtRoutes:"+JSON.stringify(fmtRoutes))
            router.addRoutes(fmtRoutes)
            console.log("所有:"+JSON.stringify(router.options.routes))
            store.commit('initRoutes', 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,
            name: name,
            iconCls: iconCls,
            meta: meta,
            children: children,
            component(resolve) {
                if (component.startsWith("Home")) {
                    require(['../views/' + component + '.vue'], resolve);
                } else if (component.startsWith("Emp")) {
                    require(['../views/emp/' + component + '.vue'], resolve);
                } else if (component.startsWith("Per")) {
                    require(['../views/per/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sal")) {
                    require(['../views/sal/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sta")) {
                    require(['../views/sta/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sys")) {
                    require(['../views/sys/' + component + '.vue'], resolve);https://next.router.vuejs.org/zh/guide/advanced/dynamic-routing.html
                }
            }
        }
        fmRoutes.push(fmRouter);
    })
    return fmRoutes;
}

①:使用router.addRoute()代替router.addRoutes()

教程中使用的是vue2(vue router3)而我目前的环境是vue3(vue router3),教程中使用router.addRoutes(fmtRoutes)将处理过的后台数据加到router内,查看router.addRoutes动态路由可以知道目前router.addRoutes()已经被router.addRoute()代替,目前不能一次性添加多条路由规则,而是一次添加一条。
于是计划遍历fmtRoutes然后用router.addRoute()挨个存入·。

	fmtRoutes.forEach(t => {
	   router.addRoute(t)
	           })

但修改完后还是报错,报错信息如下:
在这里插入图片描述
在这里插入图片描述

②:修改组件引入方式

component(resolve) {...}这块代码的主要作用是根据不同的component开头名字引入不同目录下的.vue文件,怀疑是require这种引入方式有问题,于是改成import,最终component(){...}代码块如下:

            component() {
                if (component.startsWith("Home")) {
                    return import('../views/' + component );
                } else if (component.startsWith("Emp")) {
                    return import('../views/emp/' + component);
                } else if (component.startsWith("Per")) {
                    return import('../views/per/' + component);
                } else if (component.startsWith("Sal")) {
                    return import('../views/sal/' + component);
                } else if (component.startsWith("Sta")) {
                    return import('../views/sta/' + component);
                } else if (component.startsWith("Sys")) {
                    return import('../views/sys/' + component);
                }
            }

测试:
发现员工资料下的基本资料不能正常访问(以admin账户登录),其他菜单的子项都能访问。
在这里插入图片描述
如:人事管理下的员工资料就可以正常访问
在这里插入图片描述
查看后端返回的json

[
    {
        "id": 2,
        "url": "/",
        "path": "/home",
        "component": "Home",
        "name": "员工资料",
        "iconCls": "fa fa-user-circle-o",
        "meta": {
            "keepAlive": null,
            "requireAuth": true
        },
        "parentId": 1,
        "enabled": true,
        "children": [
            {
                "id": 7,
                "url": null,
                "path": "/emp/basic",
                "component": "EmpBasic",
                "name": "基本资料",
                "iconCls": null,
                "meta": {
                    "keepAlive": null,
                    "requireAuth": true
                },
                "parentId": 2,
                "enabled": true,
                "children": null
            }
        ]
    },...

员工资料下的基本资料是存在的,而且component值为"EmpBasic"
于是把经过处理的后台数据fmtRoutes遍历,输出到控制台。

        fmtRoutes.forEach(t => {
            console.log("t:"+JSON.stringify(t))
            router.addRoute(t)
        })

在这里插入图片描述
可以看出第一个员工资料存在子项基本资料
那么问题可能在router.addRoute(t)这个过程中,用router.getRoutes()查看router加入数据之后的信息,在控制台输出。

            fmtRoutes.forEach(t => {
                console.log("t:"+JSON.stringify(t))
                router.addRoute(t)
            })
            console.log(router.getRoutes())

在这里插入图片描述

③:修改路由中重复的name值

从上图可知只存在一个name为员工资料的路由,而且path'/per/emp',怀疑是router不允许name值重复,于是在数据库中将该name值改成员工信息
测试:
在这里插入图片描述
访问正常!

二:解决在某一个子菜单页刷新,报错 No match found for location with path “xxx”

当我们解决完第一个问题时,会发现:虽然所有子菜单项目都能正常访问,但是在当前子菜单项点击刷新后,会显示空白页面,控制台继续报错No match found for location with path "xxx",以员工资料下的基本资料为例:
在这里插入图片描述
查看控制台,可以看到Vue Roter在一开始就被调用了,而component得在之后才能引入,自然就找不到了,参考next({…to, replace: true})的作用,(确保动态路由完全加载)修改代码:

①修改menus.js/initMenu方法

修改前:

export const initMenu = (router, store) => {

    if (store.state.routes.length > 0) {
        return;
    }
    getRequest("/system/config/menu").then(data => {
        if (data) {
            let fmtRoutes = formatRoutes(data);
            store.commit('initRoutes',fmtRoutes);
            fmtRoutes.forEach(t => {
                router.addRoute(t)
            })
            console.log(router.getRoutes())
        }
    })
}

修改后:

export const initMenu=(router, store,next)=>{

    if(store.state.routes == 0) {
    getRequest("/system/config/menu").then(data=>{
        if(data) {
            let fmtRoutes = formatRoutes(data);
            store.commit('initRoutes',fmtRoutes);
            fmtRoutes.forEach(t => {
                router.addRoute(t)
            })
            next({...to, replace: true})
            console.log(router.getRoutes())
        }
    })
    } else {
        next();
    }
}

②将menus.js/initMenu方法的实现移动到main.js/router.beforeEach(){…}内部,确保next({…to, replace: true})运行

修改前:

router.beforeEach((to, from,next) => {

    if (to.path == '/') {
        next();
    }else {
        if(window.sessionStorage.getItem("user")) {
        initMenu(router, store);
        next();
        } else {
            next('/?redirect='+to.path)
        }
    }
})

修改后:

router.beforeEach((to, from,next) => {

    if (to.path == '/') {
        next();
    }else {
        if(window.sessionStorage.getItem("user")) {
            if(store.state.routes == 0) {
                getRequest("/system/config/menu").then(data=>{
                    if(data) {
                        let fmtRoutes = formatRoutes(data);
                        store.commit('initRoutes',fmtRoutes);
                        fmtRoutes.forEach(t => {
                            console.log("t:"+JSON.stringify(t))
                            router.addRoute(t)
                        })
                        next({...to, replace: true})
                    }
                })
            } else {
                next();
            }
        } else {
            next('/?redirect='+to.path)
        }
    }
})

测试:刷新正常显示。
在这里插入图片描述
前端项目仓库:人事管理(前端)

Logo

前往低代码交流专区

更多推荐