vue 实现动态菜单
动态菜单的实现思路
在Vue中实现动态菜单通常涉及从后端API获取菜单数据,根据用户权限或角色动态渲染菜单项。核心思路是将菜单数据与组件分离,通过数据驱动视图。
数据准备与API交互
创建菜单数据模型,通常包含菜单名称、路径、图标、子菜单等字段。通过axios或其他HTTP库从后端获取菜单数据:
// 示例菜单数据结构
[
{
name: 'Dashboard',
path: '/dashboard',
icon: 'el-icon-menu',
children: []
},
{
name: '用户管理',
path: '/user',
icon: 'el-icon-user',
children: [
{ name: '用户列表', path: '/user/list' }
]
}
]
在Vuex或Pinia中存储菜单状态,创建action异步获取菜单数据:

// Vuex示例
actions: {
async fetchMenus({ commit }) {
const { data } = await axios.get('/api/menus')
commit('SET_MENUS', data)
}
}
递归组件实现多级菜单
创建可递归的菜单组件处理多级嵌套菜单结构:
<template>
<template v-for="item in menuData" :key="item.path">
<el-submenu v-if="item.children?.length" :index="item.path">
<template #title>
<i :class="item.icon"></i>
<span>{{ item.name }}</span>
</template>
<menu-item :menu-data="item.children"></menu-item>
</el-submenu>
<el-menu-item v-else :index="item.path">
<i :class="item.icon"></i>
<template #title>{{ item.name }}</template>
</el-menu-item>
</template>
</template>
<script>
export default {
name: 'MenuItem',
props: {
menuData: Array
}
}
</script>
路由权限控制
结合Vue Router实现动态路由添加,在路由守卫中处理权限:

router.beforeEach(async (to, from, next) => {
const hasMenus = store.getters.menus.length > 0
if (!hasMenus) {
await store.dispatch('fetchMenus')
const menus = store.getters.menus
const routes = generateRoutes(menus)
router.addRoutes(routes)
return next(to.path)
}
next()
})
function generateRoutes(menus) {
return menus.map(menu => ({
path: menu.path,
component: () => import(`@/views${menu.path}/index.vue`),
children: menu.children ? generateRoutes(menu.children) : []
}))
}
响应式菜单状态管理
使用计算属性实现菜单的响应式更新,结合权限过滤可见菜单项:
computed: {
filteredMenus() {
const { menus, permissions } = this.$store.state
return filterMenusByPermission(menus, permissions)
}
}
function filterMenusByPermission(menus, permissions) {
return menus.filter(menu => {
if (menu.permission && !permissions.includes(menu.permission)) {
return false
}
if (menu.children) {
menu.children = filterMenusByPermission(menu.children, permissions)
}
return true
})
}
菜单激活状态保持
利用Vue Router的matched属性实现菜单高亮同步:
watch: {
'$route.matched'(matched) {
this.activeMenu = matched[matched.length - 1]?.path
}
}
性能优化建议
对于大型菜单系统,可以采用虚拟滚动优化长列表性能。实现菜单数据的懒加载,只在展开父菜单时加载子菜单数据。使用keep-alive缓存常用菜单对应的组件状态。






