文章目录
前言
在现代Web应用中,动态菜单是提升用户体验的关键功能,尤其在后端管理系统或权限控制场景中。Vue 3结合Vue Router,可以轻松实现根据路由信息动态生成菜单,并通过递归组件封装成可复用的模块。本文将一步步指导你创建这样一个组件:组件外部传入路由信息,组件内部递归调用自身处理嵌套路由。整个过程结构清晰,代码可靠,适用于实际项目。
一、动态菜单的核心原理
动态菜单的核心是递归组件:菜单组件接收一个路由数组数据,使用v-for遍历每个路由项,路由path作为唯一标识,如果路由不包含子路由,直接生成菜单项;如果路由只包含一个子路由,将子路由作为菜单项;如果路由包括至少两个子路由,则先将路由meta信息中的标题作为菜单标题,再递归调用菜单组件,这样子菜单的生成就遵循前两种菜单生成规则来生成,直到生成全部菜单项。
二、实现步骤详解
1.定义路由配置
在项目中,路由信息通常在router/routes.js中定义。示例路由配置如下:
javascript
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'login',
meta: {
title: '登录',
hidden: true,
},
},
{
path: '/',
component: () => import('@/layout/index.vue'),
name: 'layout',
meta: {
title: '',
hidden: true,
},
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/home/index.vue'),
name: 'home',
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled',
},
},
],
},
{
path: '/screen',
component: () => import('@/views/screen/index.vue'),
name: 'screen',
meta: {
title: '数据大屏',
hidden: false,
icon: 'Platform',
},
},
{
path: '/acl',
component: () => import('@/layout/index.vue'),
name: 'acl',
meta: {
title: '权限管理',
hidden: false,
icon: 'Lock',
},
redirect: '/acl/user',
children: [
{
path: '/acl/user',
component: () => import('@/views/acl/user/index.vue'),
name: 'user',
meta: {
title: '用户管理',
hidden: false,
icon: 'User',
},
},
{
path: '/acl/role',
component: () => import('@/views/acl/role/index.vue'),
name: 'role',
meta: {
title: '角色管理',
hidden: false,
icon: 'UserFilled',
},
},
{
path: '/acl/menu',
component: () => import('@/views/acl/menu/index.vue'),
name: 'menu',
meta: {
title: '菜单管理',
hidden: false,
icon: 'List',
},
},
],
},
{
path: '/product',
component: () => import('@/layout/index.vue'),
name: 'product',
meta: {
title: '商品管理',
hidden: false,
icon: 'Goods',
},
redirect: '/product/brand',
children: [
{
path: '/product/brand',
component: () => import('@/views/product/brand/index.vue'),
name: 'brand',
meta: {
title: '品牌管理',
hidden: false,
icon: 'ShoppingCartFull',
},
},
{
path: '/product/attribute',
component: () => import('@/views/product/attribute/index.vue'),
name: 'attribute',
meta: {
title: '属性管理',
hidden: false,
icon: 'SetUp',
},
},
{
path: '/product/spu',
component: () => import('@/views/product/spu/index.vue'),
name: 'spu',
meta: {
title: 'SPU管理',
hidden: false,
icon: 'Suitcase',
},
},
{
path: '/product/sku',
component: () => import('@/views/product/sku/index.vue'),
name: 'sku',
meta: {
title: 'SKU管理',
hidden: false,
icon: 'CreditCard',
},
},
],
},
{
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404',
meta: {
title: '404',
hidden: true,
},
},
{
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'notfound',
meta: {
title: 'notfound',
hidden: true,
},
},
]
2.创建递归菜单组件
javascript
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
defineProps(['menuList'])
const goRoute = (menuItem) => {
router.push(menuItem.index)
}
</script>
<script>
export default {
name: 'Menu',
}
</script>
<template>
<!-- 根据路由动态递归生成菜单 -->
<template
v-for="item in menuList"
:key="item.path"
>
<!-- 没有子菜单的菜单项 -->
<template v-if="!item.children">
<el-menu-item
v-if="!item.meta.hidden"
:index="item.path"
@click="goRoute"
>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 只有一个子菜单的菜单项 -->
<template v-if="item.children && item.children.length === 1">
<el-menu-item
v-if="!item.children[0].meta.hidden"
:index="item.children[0].path"
@click="goRoute"
>
<el-icon>
<component :is="item.children[0].meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.children[0].meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 多个子菜单的菜单项 -->
<el-sub-menu
v-if="item.children && item.children.length > 1"
:index="item.path"
>
<template #title>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
<!-- 递归生成子菜单 -->
<Menu :menuList="item.children"></Menu>
</el-sub-menu>
</template>
</template>
<style scoped></style>
3.在父组件中使用动态菜单
javascript
<script setup>
import Menu from './menu/index.vue'
import { constantRoutes } from '@/router/routes.js'
...
</script>
<template>
...
<Menu :menuList="constantRoutes"></Menu>
...
</template>
总结
通过封装递归组件,Vue 3能高效实现动态菜单:外部传入路由信息,内部递归处理嵌套结构。这不仅减少了代码冗余,还提高了可维护性。在实际项目中,你可以根据需求扩展功能,如添加图标、权限校验等。