vue-element-admin
vue-element-admin copied to clipboard
多级路由嵌套 keep-alive 失效
多标签间切换有时能保存之前的数据有时不行
这出现的原因是多级路由导致的,就是router-view嵌套 在层级不同的router-view中切换tag会出现缓存数据失效的问题。比如你在综合table和table内联两者之间切换不会有问题,但你和table之外的页面切换时就会有问题。我之后想想有没有什么解决方案吧。
你的缓存数据是用什么存的 我之前react 做标签数据是直接存在state里 没有这种问题
目前使用 vue 的 keep-alive. 当然你也可以和你之前的做法一样,存在vue的vuex里。
这是keep-alive的bug 么 网上查了很多资料 都没有这方面的问题
解决了 原来是被你设置了cachedViews 删掉cachedViews 所有子页面全都keep-alive 就行了 或者是你cachedViews里面的代码有bug 修复一下 应该是可以的。
不算是bug,就如我前面所说的多久层级切换的时候会让缓存失效。你是全页面keep-alive 所以没有这个问题。
那请问有没有办法使得多级路由的keep-alive依然有效呢? 楼主说的去掉cachedViews,试了下,会导致就算关闭了tag再次打开依然会缓存之前的内容,这不是我想要的。
每个多级目录的component指向同一个.vue,就可以做到不同的多级目录之间共享keep-alive,但是它们与根目录之间还是不能共享keep-alive。即只有同一个router-view之间才能共享keep-alive
@hoganfan https://github.com/PanJiaChen/vue-element-admin/commit/d431de058943ad653029f90892954ad96274fe20 你可以借鉴一下,这个分支一直没有合。
您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。
如下图:
您好,我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。
如下图:
你好 能看下你的代码吗?
那请问有没有办法使得多级路由的keep-alive依然有效呢? 楼主说的去掉cachedViews,试了下,会导致就算关闭了tag再次打开依然会缓存之前的内容,这不是我想要的。
请问关闭tag,打开后依旧会缓存这个问题你解决了吗,我还发现右键刷新第二次就重定向不到标签了
即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。
我也有这个问题,不知道好了吗
您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。
如下图:
您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。
如下图:
只在state.cachedViews 加上,会在关掉之后的 DEL_ALL_VIEWS中再次将 state.cachedViews 清空,所以在后续清空中继续设置一遍就好了。
DEL_ALL_VIEWS: (state) => { state.visitedViews = [] state.cachedViews = ['empty'] }
cachedViews: ['empty']
建议作者可以默认包含一个什么都不做的route-view,然后state.cachedViews 从空数组改为一个包含空route-view的数组
即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。
这个确实会 请问你解决了吗?
即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。
这个确实会 请问你解决了吗?
这个问题我自己解决了,之前也想了好多办法,比如用vuex来做缓存等等,发现都太麻烦。
最后偶然想到,实际上是不是多级菜单对于项目业务来讲并不重要,只是对于后台菜单显示上才重要,也就是说,多级菜单实际上就是为了界面显示而分成的多级。
既然这样的话我用把显示的菜单和实际的路由分离开就可以了, 菜单的显示继续用多级的菜单数据, 然后router里面实际添加的数据进行格式化一下,全部转换成一级菜单, 这样就不涉及到父菜单需要用router-view来显示子级菜单内容, 所有的页面都共用的一个router-view, 就没这个缓存的问题了。
即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。
这个确实会 请问你解决了吗?
这个问题我自己解决了,之前也想了好多办法,比如用vuex来做缓存等等,发现都太麻烦。 最后偶然想到,实际上是不是多级菜单对于项目业务来讲并不重要,只是对于后台菜单显示上才重要,也就是说,多级菜单实际上就是为了界面显示而分成的多级。 既然这样的话我用把显示的菜单和实际的路由分离开就可以了, 菜单的显示继续用多级的菜单数据, 然后router里面实际添加的数据进行格式化一下,全部转换成一级菜单, 这样就不涉及到父菜单需要用router-view来显示子级菜单内容, 所有的页面都共用的一个router-view, 就没这个缓存的问题了。
你好,可以看看你的关键代码吗?
即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。
这个确实会 请问你解决了吗?
这个问题我自己解决了,之前也想了好多办法,比如用vuex来做缓存等等,发现都太麻烦。 最后偶然想到,实际上是不是多级菜单对于项目业务来讲并不重要,只是对于后台菜单显示上才重要,也就是说,多级菜单实际上就是为了界面显示而分成的多级。 既然这样的话我用把显示的菜单和实际的路由分离开就可以了, 菜单的显示继续用多级的菜单数据, 然后router里面实际添加的数据进行格式化一下,全部转换成一级菜单, 这样就不涉及到父菜单需要用router-view来显示子级菜单内容, 所有的页面都共用的一个router-view, 就没这个缓存的问题了。
你好,可以看看你的关键代码吗?
我的不是用vue-element-admin做的,当时比较早,还没有vue-element-admin,自己搭的框架。 解决缓存的问题原理挺简单的, 缓存失效是因为父级需要为子级菜单提供一个容器(router-view), 就造成了多层嵌套keep-alive,如果一个设置了缓存,一个没有设置缓存, 或者从一个子级菜单切换到另外一个不同父容器的菜单,都会造成已缓存的组件被销毁掉。 但是如果都共用一个keep-alive,就能解决这个问题。
我这边的解决办法是这样的,首先将配置好的router,用vuex缓存起来,用作菜单的显示。 然后把router转换一下,全部转换成一级,再router.addRoutes, 这样菜单的显示和实际操作的路由就分离了,一个是多级,一个是一级。 至于面包屑导航,是在router.afterEach, 通过route.name 查找了一遍 菜单的数据重新生成的。
这个问题解决了吗?
您好,我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。 如下图:
你好 能看下你的代码吗?
您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。
如下图:
使用这种方式的话会有一个问题,假如有两个三级路由依赖这个二级路由的组件的话,需要同时关闭这两个三级路由才会清空缓存,不然你关闭tab重新打开的话缓存依然存在
我做的时候,换了个思路,menu和router是分离的,索性就把数据搞成两套。然后重新生成一套供vue-router使用的始终保持两层的数据,就规避了三层的问题。menu数据还是作者的数据没动。(我项目的菜单登陆时候动态获取的,我在登陆成功拿到菜单数据之后format了两次,有个问题是:第二层级是不能跳转的😂,感觉跟楼上的思路都差不多)
@bluegek 能看下你的具体解决方法吗
原本keep-alive是按照组件名缓存。
如果仅作用于页面组件时,结合vue-router
,可改为按照当前路由路径(route.path
)做缓存;Fork一份keep-alive.js,将相关部分做修改。但是可能对动态路由无法支持。
我这边将菜单显示跟路由做了分离,目前使用下来没什么问题。
`export const initMenu = (router, menu) => { if (menu.length === 0) { return } const menus = formatRoutes(menu)
const routeMenu = formatRoutes(filterAsyncRouter(menu))
const unfound = { path: '*', redirect: '/404', hidden: true } menus.push(unfound) routeMenu.push(unfound) store.commit('ADD_ROUTERS', menus) // 菜单显示正常
router.addRoutes(routeMenu) }
// 遍历后台传来的路由字符串,转换为组件对象 function filterAsyncRouter (asyncRouterMap, lastRouter = false, type = true) { return JSON.parse(JSON.stringify(asyncRouterMap)).filter(route => { // 处理 vue-router所需要路由 Empty(继承Empty模板的层)的children全部提到上一层 if (type && route.children) { route.children = filterChildren(route.children) } // 拼装路由 if (lastRouter && route.path.indexOf('http') === -1) { route.path = lastRouter.path + '/' + route.path } if (route.children != null && route.children && route.children.length) { route.children = filterAsyncRouter(route.children, route, type) } return true }) } // function filterChildren (childrenMap, lastRouter = false) { var children = [] JSON.parse(JSON.stringify(childrenMap)).forEach((el, index) => { if (el.children && el.children.length) { if (el.component === 'Empty') { el.children.forEach(c => { c.path = el.path + '/' + c.path if (c.children && c.children.length) { children = children.concat(filterChildren(c.children, c)) return } children.push(c) }) childrenMap.splice(index, 1) return } } if (lastRouter) { el.path = lastRouter.path + '/' + el.path } children = children.concat(el) }) return children }`
二级菜单那一层的router-view中再加个keep-alive,同时设置二级菜单的组件name
这个问题已经解决,思路跟楼上的一样,核心就是给菜单展示和真正的路由区分开来,真正的路由最多到二级公用一个router-view,就能实现缓存了
// 扁平化路由,在添加路由方法`router.addRoutes`前处理.
// 因keep-alive无法对超过二级的页面保存状态,
// 参见BUG:https://github.com/PanJiaChen/vue-element-admin/issues/406
// 解决方案:显示菜单使用三级,系统路由将三级路由移动到二级
export function flatRoutes(routes:RouteConfig[]){
let ret = [];
routes.forEach(it=>{
const r = {...it,children:[]};
it.children.forEach(it2=> {
// 把三级移动到二级,以避免keep-alive无法缓存多级路由的状态
if (it2.children && it2.children.length > 0) {
it2.children.forEach(it3 => r.children.push({...it3, children: null}));
return;
}
// 直接添加二级路由
r.children.push({...it2, children: null});
});
ret.push(r);
});
return ret;
}
在添加路由前转换:
const finalRoutes = flatRoutes(PermissionModule.dynamicRoutes);
router.addRoutes(finalRoutes);
// 扁平化路由,在添加路由方法`router.addRoutes`前处理. // 因keep-alive无法对超过二级的页面保存状态, // 参见BUG:https://github.com/PanJiaChen/vue-element-admin/issues/406 // 解决方案:显示菜单使用三级,系统路由将三级路由移动到二级 export function flatRoutes(routes:RouteConfig[]){ let ret = []; routes.forEach(it=>{ const r = {...it,children:[]}; it.children.forEach(it2=> { // 把三级移动到二级,以避免keep-alive无法缓存多级路由的状态 if (it2.children && it2.children.length > 0) { it2.children.forEach(it3 => r.children.push({...it3, children: null})); return; } // 直接添加二级路由 r.children.push({...it2, children: null}); }); ret.push(r); }); return ret; }
在添加路由前转换:
const finalRoutes = flatRoutes(PermissionModule.dynamicRoutes); router.addRoutes(finalRoutes);
完美解决问题