vue路由面包屑方案

1,233 阅读4分钟

最近因为转战到新公司前端技术部门需要使用vue,不得已从angular转型vue。搭建后台框架生成面包屑导航的时候遇到了一个问题。以下是问题记录。

众所周知,在angular中功能是分为模块化的,我们可以将路由地址指向某个模块。如下图所示

image.png

image.png

最终home路由和home/add都是在同一个路由层级,在生成面包屑导航是可以根据层级划分进行查找路由。 但是vue没有这么简单,众所周知vue的路由是扁平化的,如果你想要在当前路由内部再次嵌套一层路由,最好的方式就是写一个子路由如下图所示。

image.png

对应的我们需要在相应路由添加一个路由出口。 到现在为止看上去还都是很合理,但是我们不妨想象一下,如果我们是一个后台管理页面,所有的界面都是在page里面展示,这个时候我们有一个功能同时拥有一个list功能页,一个add功能页,并且add功能极其繁琐需要单独进入一个页面进行操作,那么这个时候问题来了,我们需要在头部生成一个面包屑导航,如果我们将list与add路径一起放在page子路径中,那么add向上查找时势必查找不到对应的list数据,如果我们单独写一个子路由,那么就要在当前页面中新增一个无用组件来设置路由出口,并不利于维护,相对也比较繁琐。于是我进行了字符匹配,进行路由检索,并且与同事约定好路由名称,根据相匹配名称来约定父子级关系,如下图所示。

image.png

子级路由前缀为约定好的命名空间,路由切换的时候进行检索,强行进行匹配,这种方法简单粗暴,只需要我们定义好路由,无需再重新指定路由出口,或者新增空页面。 但是相对应的问题随之而来,因为我是根据path进行匹配的,所以不能指定路由动态传参。 所以约定了一种新的路由数据结构,当前数据结构堪堪完成,具体操作以后更新......

const routerObj = {
    pages: {
        router: {
            path: '/pages',
            component: () => import('@/pages/pages.vue'),
            meta: {
                title: '后台页面'
            },
        },
        home: {
            router: {
                component: () => import('@/pages/home/home.vue'),
                meta: {
                    title: '首页'
                },
            }
        },
        newPageSetup: {
            router: {
                component: () => import('@/pages/newPageSetup/newPageSetup.vue'),
                meta: {
                    title: '页面搭建',
                    isReuse: true
                }
            },
            add: {
                router: {
                    component: () => import('@/pages/newPageSetup/addNewPageSetup.vue'),
                    meta: {
                        title: '新建页面'
                    }
                },
            }
        },
        activity: {
            router: {
                component: () => import('@/pages/activity/activityList.vue'),
                meta: {
                    title: '活动管理',
                    isReuse: true
                }
            },
            detail: {
                router: {
                    component: () => import('@/pages/activity/activityDetail.vue'),
                    meta: {
                        title: '活动详情'
                    }
                },
            },
            add: {
                router: {
                    component: () => import('@/pages/announcement/addAnnouncement.vue'),
                    meta: {
                        title: '新建公告',
                    }
                }
            }
        },
        activityApprove: {
            router: {
                component: () => import('@/pages/activity/activityApprove.vue'),
                meta: {
                    title: '活动审批',
                    isReuse: true
                }
            }
        },
        announcement: {
            router: {
                component: () => import('@/pages/announcement/announcementList.vue'),
                meta: {
                    title: '公告管理',
                    isReuse: true
                }
            },
            add: {
                router: {
                    component: () => import('@/pages/announcement/addAnnouncement.vue'),
                    meta: {
                        title: '新建公告',
                    }
                }
            }
        },
        approve: {
            router: {
                component: () => import('@/pages/approve/approve.vue'),
                meta: {
                    title: '审批流管理',
                    isReuse: true
                }
            }
        },
        label: {
            router: {
                component: () => import('@/pages/label/labelManagement.vue'),
                meta: {
                    title: '标签管理',
                    isReuse: true
                }
            }
        },
        accoutManageList: {
            router: {
                component: () => import('@/pages/accoutManage/accoutManageList.vue'),
                meta: {
                    title: '账户管理列表',
                    isReuse: true
                }
            }
        },
        jurisdiction: {
            router: {
                component: () => import('@/pages/jurisdiction/jurisdiction.vue'),
                meta: {
                    title: '权限管理',
                    isReuse: true
                }
            }
        },
        sealManage: {
            router: {
                component: () => import('@/pages/seal/sealManage.vue'),
                meta: {
                    title: '印章管理',
                    isReuse: true
                }
            }
        },
        classRoomManage: {
            router: {
                component: () => import('@/pages/classRoomManage/classRoomManage.vue'),
                meta: {
                    title: '课堂管理',
                    isReuse: true
                }
            }
        },
        courseware: {
            router: {
                component: () => import('@/pages/courseware/courseware.vue'),
                meta: {
                    title: '课件管理',
                    isReuse: true
                }
            }
        },
        curriculum: {
            router: {
                component: () => import('@/pages/curriculum/curriculum.vue'),
                meta: {
                    title: '课程管理',
                    isReuse: true
                }
            }
        }
    }
};

// 路由数组
let arrRouter: any = [];

// 设置父指针
function setObj(routerObj: any): void {
    // 遍历对象
    Object.keys(routerObj).forEach((item: string) => {
        // 查找当前对象每一项
        const pages = {...routerObj[item]};
        // 如果对象存在并且不是父指针或者路由
        if (pages && item !== 'router' && item !== 'parent') {
            if (pages.router?.name === undefined) {
                pages.router = {
                    ...pages.router,
                    name: item,
                    path: pages.router.path !== '/pages' ? (pages.router.path || item) : '',
                    meta: {
                        ...pages.router.meta,
                        pathIndex: item
                    }
                }
            }
            // 遍历对象子集并且将自己父指针指向当前对象并且进行递归
            Object.keys(pages).forEach(data => {
                if (pages[data] && data !== 'router' && data !== 'parent') {
                    pages[data].parent = pages;
                    // 设置路由name
                    pages[data].router.name = pages.router.name + data.substring(0,1).toUpperCase() + data.substring(1);
                    // 设置路由路径
                    if (pages.router.path === '') {
                        pages[data].router.path = data;
                    } else {
                        pages[data].router.path = pages.router.path + '/' + (pages[data].router.path || data);
                    }
                    // 设置路由索引
                    pages[data].router.meta.pathIndex = pages.router.meta.pathIndex + '/' + data;
                    // 将组装好的路由添加至路由数组
                    arrRouter.push(pages[data].router);
                }
            })
            setObj(pages)
        }
    });
}

// 开始递归
setObj(routerObj);

console.log(routerObj);

export default arrRouter;
console.log(arrRouter);

最终会在当前meta中生成一个pathIndex索引字段如下图所示

image.png 我们可以从索引字段中查询到当前路由对象,再根据父指针生成面包屑导航。 项目地址:gitee.com/usercxx/vue…