– 先看看router.js
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ // mode: 'hash', routes: [ { path: '/', component: () => import('@/views/layout/index.vue'), redirect: '/index', children: [ { path: 'index', name: 'index', component: () => import('@/views/index'), meta: { target: 'router', icon: "el-icon-s-promotion", title: "首页", allPath: "/index" } } ] }, { path: '/error404', component: () => import('@/views/error/404.vue') } ] }) // 这里提醒一下,以上代码是根据我工作里实际情况来了,也就是基于后端返回的数据结构来的,你们具体如何要看实际情况. 你在阅读此文章时只需要参考思路即可. 我的菜单都是要放在根路径'/'下的children里的,如果有一个和首页同级的一级菜单那么就是在routes[0].children里push进去 解释完了就继续吧 现在我们看看vuex里是什么样 import router from '@/router' export default { state: { isinitRoutes: false, // 用来判断是否初始化过的标识符 routes: [ { path: '/', component: () => import('@/views/layout/index.vue'), redirect: '/theme/theme', children: [] } ] }, mutations: { UPDATAMENUS (state, data) { let newData = [...data] // 因为我获取来的数据以菜单为基准,包括了router里事件定义过的首页,避免重复添加所以删除第一个 newData.shift() router.addRoutes([{ path: '/', component: () => import('@/views/layout/index.vue'), children: newData }]) state.routes[0].children = data state.isinitRoutes = true } }, actions: { updateMenus ( { commit }, data ) { commit('UPDATAMENUS', data) } } } 上面的代码其实应该最后看,接着我们看看layout.vue里的代码,先看生成菜单部分 <template> <el-menu class="el-menu-vertical-demo" :default-active="$router.path" :collapse='isCollapse' @open='handleOpen'> <!-- 侧边菜单渲染 --> <sidebar-item v-if="routes && routes[0] && routes[0].children" :submenus="routes[0].children"></sidebar-item> </el-menu> </template> import sidebarItem from './components/sidebarItem.vue' import { getMenus } from '@/api/layout.js' import { mapGetters, mapActions } from "vuex" export default { components: { sidebarItem }, computed: { ...mapGetters(['routes', 'isinitRoutes', 'edit']), }, created () { this.initMenu(); }, methods: { ...mapActions(['updateMenus']), initMenu() { if (!this.isinitRoutes) { getMenus().then(res => { // 调用vuex actions方法设置菜单和路由 this.updateMenus(this.recursiveData(res.funcList)) }) } }, // 菜单数据递归处理方法 动态添加路由 recursiveData (data) { let result = [] function resolveData (oldMenus,menus) { oldMenus.forEach((ele, index) => { if (ele.children.length > 0) { let item = { path: ele.path, name: ele.name, component: () => import(`@/views/${ele.component}`), meta: { target: ele.target, icon: ele.icon, title: ele.title, allPath: ele.allPath, url: ele.url }, children: [] } menus.push(item) resolveData(ele.children, menus[index].children) } else { let item = { path: ele.path, name: ele.name, component: () => import(`@/views/${ele.component}`), meta: { target: ele.target, icon: ele.icon, title: ele.title, allPath: ele.allPath, url: ele.url } } menus.push(item) } }) } resolveData(data, result) return result }, // 伸缩侧边菜单 collapseNavmenu () { this.isCollapse = !this.isCollapse; } } } 再看一下sidebar-item这个子组件 <template> <div> <template v-for="item in submenus"> <el-menu-item v-if="Object.keys(item).indexOf('children') < 0" :key="'menuItem'+item.path" :index="item.meta.allPath"> <i :class="item.meta.icon"></i> <router-link class="text" v-if="item.meta.target == 'router'" :to="item.meta.allPath">{{ item.meta.title }}</router-link> // 这里是我司业务需求,需要接入一些老项目,有的菜单不能走vue-router跳转,需要通过iframe或者新打开一个页面,因此这里加了判断 <a class="text" v-else :href="item.meta.url" :target="item.meta.target">{{ item.meta.title }}</a> </el-menu-item> <el-submenu v-else :key="'submenu'+item.path" :index="item.meta.allPath" :class="{ 'last-sub': item.children }"> <template slot="title"> <i :class="item.meta.icon"></i> <span class="text">{{ item.meta.title }}</span> </template> <sidebar-item :submenus="item.children"></sidebar-item> </el-submenu> </template> </div> </template> <script> export default { name:'sidebarItem', props: { submenus: { type: Array } } }至此就介绍完了,但是特别注意一个地方
通过addRoutes动态添加路由时,如果component不确定需要根据后台数据或者变量决定的话,一定要写成这样 component: () => import(`@/views/${ele.component}`) 如果写成component: () => import(“ele.component”),就不行,这是因为webpack并不能完全动态编译,它需要你给他指定一个路径,因此我们可以用静态和动态数据拼接的方式
还有就是记得吧路由或菜单数据存到sessionStorage里去,否则手动刷新后,因为没有数据会导致当前页面为空