vue实现动态菜单
实现动态菜单的基本思路
动态菜单通常指根据用户权限、角色或后端配置动态生成导航菜单。Vue中可通过以下方式实现:
-
定义菜单数据结构 后端返回的菜单数据通常为树形结构,例如:
[ { "name": "Dashboard", "path": "/dashboard", "icon": "el-icon-menu", "children": [] }, { "name": "User Management", "path": "/user", "icon": "el-icon-user", "children": [ { "name": "List", "path": "/user/list" } ] } ] -
存储菜单状态 使用Vuex或Pinia管理菜单状态:
// store/modules/menu.js const state = { menus: [] } const mutations = { SET_MENUS(state, menus) { state.menus = menus } } const actions = { async fetchMenus({ commit }) { const res = await api.getMenus() commit('SET_MENUS', res.data) } }
递归组件实现菜单渲染
-
创建菜单组件 使用递归组件处理多级菜单:
<template> <div v-for="item in menuData" :key="item.path"> <el-submenu v-if="item.children" :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> <span>{{ item.name }}</span> </el-menu-item> </div> </template> <script> export default { name: 'MenuItem', props: { menuData: { type: Array, required: true } } } </script> -
主布局中使用菜单 在布局组件中引入动态菜单:
<template> <el-container> <el-aside> <el-menu router :default-active="$route.path"> <menu-item :menu-data="menus"></menu-item> </el-menu> </el-aside> </el-container> </template> <script> import { mapState } from 'vuex' import MenuItem from './MenuItem.vue' export default { components: { MenuItem }, computed: { ...mapState(['menus']) }, created() { this.$store.dispatch('fetchMenus') } } </script>
权限控制实现
-
路由拦截 结合路由守卫进行权限验证:
router.beforeEach((to, from, next) => { const hasPermission = checkPermission(to.meta.roles) if (!hasPermission) { next('/403') } else { next() } }) -
菜单过滤 根据用户角色过滤菜单项:
function filterMenus(menus, roles) { return menus.filter(menu => { if (menu.meta && menu.meta.roles) { return menu.meta.roles.some(role => roles.includes(role)) } return true }).map(menu => { if (menu.children) { menu.children = filterMenus(menu.children, roles) } return menu }) }
性能优化建议
-
菜单缓存 避免每次路由切换都重新计算菜单:
// 在store中缓存过滤后的菜单 getters: { filteredMenus: (state) => { return filterMenus(state.menus, state.user.roles) } } -
按需加载图标 使用动态图标加载减少初始包大小:
const iconComponents = { 'dashboard': () => import('@/icons/Dashboard.vue'), 'user': () => import('@/icons/User.vue') } // 在组件中使用 <component :is="iconComponents[item.icon]" />
错误处理方案
-
菜单加载失败处理 添加错误状态和重试机制:
const actions = { async fetchMenus({ commit }) { try { const res = await api.getMenus() commit('SET_MENUS', res.data) } catch (error) { commit('SET_MENU_ERROR', true) } } } -
默认菜单配置 准备静态菜单作为fallback:

const defaultMenus = [ { name: 'Home', path: '/' } ]






