通常的后台管理系统功能都比较繁多,存在有多级菜单的需求,可是在这种项目里每每keep-alive的表现却很是不稳定,有时候某个页面能够缓存,可是点几下就发现缓存丢了;有时候不知道怎么回事又死活不缓存了。vue
形成这个问题的缘由是: 多级路由组件嵌套。
具体分析: 假如一个后台管理系统,有一个main.vue是全部页面的框架, 里面有这样一段代码vuex
<keep-alive> <router-view v-if="$route.meta.keepAlive"></router-view> </keep-alive> <router-view v-if="!$route.meta.keepAlive"></router-view>
而后创建了一个父级菜单,页面是ChildView.vue, 里面的代码以下缓存
<router-view></router-view>
假设这个父菜单里有两个子菜单,分别为 A和B, 而后A设置为缓存(keepAlive=true), B不缓存(keepAlive=false);
当点击A菜单的时候,系统缓存了ChildView.vue组件。
当点击B菜单的时候,因为B设置的不缓存,因此致使ChildView.vue组件被销毁
这就是keep-alive为何会失效的根本缘由。框架
还有更复杂的,好比同一个菜单里的子菜单能够缓存,可是点击另一个父菜单或者父菜单下的子菜单却不缓存, 究其原理和上面的分析是同样的缘由,就是多级菜单,多个共用组件致使的keepAlive缓存失效, keepAlive根本没考虑到页面缓存的复杂性。异步
如下几种表现也是这个问题形成的缘由之一:ide
分析问题:
既然是多个router-view嵌套而且共用的状况下形成的,那么若是只存在一个router-view,也就是只须要main.vue做为框架内全部页面的容器,就不会有这个问题。code
其实是不是多级菜单对于项目或者业务上来说一点都不影响,只是界面显示上须要,让用户能更快点击到本身须要的功能页面而已。 既然这样的话,显示的菜单保留多级的,实际的router弄成一级, 将显示菜单和业务router分离开。orm
解决问题:
首先将配置好的多级router用vuex缓存起来,用做展现的菜单。
而后将router转换一下,转换成一级菜单,用addRoutes异步添加到router里面。router
局部示例代码:接口
const formatRouter = (routes, newRoutes = []) => { routes.map(item => { if (item.children && item.children.length > 0) formatRouter(item.children, newRoutes); newRoutes.push(item); }) return newRoutes; } let flatRoutes = formatRouter(routes); router.addRoutes(flatRoutes);
而后面包屑导航要调整一下,大部分逻辑都是从route.matched里面获取的,可是如今router全是一级的,咱们要从展现的菜单数据里面拿面包屑导航数据。
示例代码:
/** * 自定义查找字段, 根据最后一级某个字段查找完整树(整个父类) * @param {*} val 要查找对比的值 * @param {*} data 要查找的数据 * @param {*} fKey 要查找对比的字段 */ const recursiveTreeByLastLevel = (val, data, fKey = 'value') => { let rData = []; for (let i = 0, len = data.length; i < len; i++) { rData.push(data[i]); if (data[i].children && data[i].children.length > 0) { rData = rData.concat(recursiveTreeByLastLevel(val, data[i].children, fKey)); if (rData.some(item => item[fKey] === val)) return rData; } if (data[i][fKey] === val) return rData; rData = []; } return rData; } router.afterEach((to, from, next) => { var routerList = recursiveTreeByLastLevel(to.name, store.state.sidebarMenu, 'name') store.commit('setCrumbList', routerList) // 经过vuex缓存 })
搞完,收工!