Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

多级路由嵌套 keep-alive 失效 #406

Open
a13514097 opened this issue Jan 18, 2018 · 34 comments
Open

多级路由嵌套 keep-alive 失效 #406

a13514097 opened this issue Jan 18, 2018 · 34 comments

Comments

@a13514097
Copy link

多标签间切换有时能保存之前的数据有时不行

@a13514097
Copy link
Author

image

@PanJiaChen
Copy link
Owner

这出现的原因是多级路由导致的,就是router-view嵌套 在层级不同的router-view中切换tag会出现缓存数据失效的问题。比如你在综合table和table内联两者之间切换不会有问题,但你和table之外的页面切换时就会有问题。我之后想想有没有什么解决方案吧。

@a13514097
Copy link
Author

你的缓存数据是用什么存的 我之前react 做标签数据是直接存在state里 没有这种问题

@PanJiaChen
Copy link
Owner

目前使用 vue 的 keep-alive. 当然你也可以和你之前的做法一样,存在vue的vuex里。

@a13514097
Copy link
Author

这是keep-alive的bug 么 网上查了很多资料 都没有这方面的问题

@a13514097
Copy link
Author

解决了 原来是被你设置了cachedViews 删掉cachedViews 所有子页面全都keep-alive 就行了 或者是你cachedViews里面的代码有bug 修复一下 应该是可以的。

@PanJiaChen
Copy link
Owner

不算是bug,就如我前面所说的多久层级切换的时候会让缓存失效。你是全页面keep-alive 所以没有这个问题。

@PanJiaChen PanJiaChen changed the title 多标签间切换有时能保存之前的数据有时不行 多级路由嵌套 keep-alive 失效 Jan 24, 2018
@hoganfan
Copy link

那请问有没有办法使得多级路由的keep-alive依然有效呢?
楼主说的去掉cachedViews,试了下,会导致就算关闭了tag再次打开依然会缓存之前的内容,这不是我想要的。

@hoganfan
Copy link

每个多级目录的component指向同一个.vue,就可以做到不同的多级目录之间共享keep-alive,但是它们与根目录之间还是不能共享keep-alive。即只有同一个router-view之间才能共享keep-alive

@PanJiaChen
Copy link
Owner

@hoganfan d431de0
你可以借鉴一下,这个分支一直没有合。

@hanshou101
Copy link

您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。

如下图:

image

@souths
Copy link

souths commented Oct 25, 2018

您好,我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。

如下图:

图片

你好 能看下你的代码吗?

@waittingbar
Copy link

那请问有没有办法使得多级路由的keep-alive依然有效呢?
楼主说的去掉cachedViews,试了下,会导致就算关闭了tag再次打开依然会缓存之前的内容,这不是我想要的。

请问关闭tag,打开后依旧会缓存这个问题你解决了吗,我还发现右键刷新第二次就重定向不到标签了

@TY-LIU
Copy link

TY-LIU commented Jan 7, 2019

即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。

@xlb
Copy link

xlb commented May 28, 2019

我也有这个问题,不知道好了吗

@xlb
Copy link

xlb commented May 28, 2019

您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。

如下图:

image

您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。

如下图:

image

只在state.cachedViews 加上,会在关掉之后的 DEL_ALL_VIEWS中再次将 state.cachedViews 清空,所以在后续清空中继续设置一遍就好了。

DEL_ALL_VIEWS: (state) => { state.visitedViews = [] state.cachedViews = ['empty'] }
cachedViews: ['empty']

建议作者可以默认包含一个什么都不做的route-view,然后state.cachedViews 从空数组改为一个包含空route-view的数组

@cottengc
Copy link

即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。

这个确实会 请问你解决了吗?

@myshumin
Copy link

myshumin commented Aug 5, 2019

即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。

这个确实会 请问你解决了吗?

这个问题我自己解决了,之前也想了好多办法,比如用vuex来做缓存等等,发现都太麻烦。
最后偶然想到,实际上是不是多级菜单对于项目业务来讲并不重要,只是对于后台菜单显示上才重要,也就是说,多级菜单实际上就是为了界面显示而分成的多级。
既然这样的话我用把显示的菜单和实际的路由分离开就可以了, 菜单的显示继续用多级的菜单数据, 然后router里面实际添加的数据进行格式化一下,全部转换成一级菜单, 这样就不涉及到父菜单需要用router-view来显示子级菜单内容, 所有的页面都共用的一个router-view, 就没这个缓存的问题了。

@cottengc
Copy link

cottengc commented Aug 5, 2019

即使手动追加到cacheViews里面二级菜单router-view的name,在切换到任何一个路由时候,cacheViews里面二级菜单的子页面mounted都将会被执行一次。

这个确实会 请问你解决了吗?

这个问题我自己解决了,之前也想了好多办法,比如用vuex来做缓存等等,发现都太麻烦。
最后偶然想到,实际上是不是多级菜单对于项目业务来讲并不重要,只是对于后台菜单显示上才重要,也就是说,多级菜单实际上就是为了界面显示而分成的多级。
既然这样的话我用把显示的菜单和实际的路由分离开就可以了, 菜单的显示继续用多级的菜单数据, 然后router里面实际添加的数据进行格式化一下,全部转换成一级菜单, 这样就不涉及到父菜单需要用router-view来显示子级菜单内容, 所有的页面都共用的一个router-view, 就没这个缓存的问题了。

你好,可以看看你的关键代码吗?

@myshumin
Copy link

myshumin commented Aug 5, 2019

即使手动追加到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 查找了一遍 菜单的数据重新生成的。

liangminhua pushed a commit to liangminhua/vue-element-admin that referenced this issue Nov 15, 2019
liangminhua added a commit to liangminhua/vue-element-admin that referenced this issue Nov 15, 2019
liangminhua pushed a commit to liangminhua/vue-element-admin that referenced this issue Nov 18, 2019
liangminhua pushed a commit to liangminhua/vue-element-admin that referenced this issue Nov 18, 2019
liangminhua pushed a commit to liangminhua/vue-element-admin that referenced this issue Nov 18, 2019
@Codezero123
Copy link

这个问题解决了吗?

liangminhua pushed a commit to liangminhua/vue-element-admin that referenced this issue Dec 14, 2019
liangminhua pushed a commit to liangminhua/vue-element-admin that referenced this issue Dec 14, 2019
@shanzhaozhen
Copy link

您好,我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。
如下图:
图片

你好 能看下你的代码吗?

您好。我这边的解决办法,就是在【tagsView.js】文件的state.cacheViews里,手动加上一级菜单和三级菜单之间,缺失的二级菜单的名字。

如下图:

image

使用这种方式的话会有一个问题,假如有两个三级路由依赖这个二级路由的组件的话,需要同时关闭这两个三级路由才会清空缓存,不然你关闭tab重新打开的话缓存依然存在

@lucacicii
Copy link

lucacicii commented Mar 9, 2020

我做的时候,换了个思路,menu和router是分离的,索性就把数据搞成两套。然后重新生成一套供vue-router使用的始终保持两层的数据,就规避了三层的问题。menu数据还是作者的数据没动。(我项目的菜单登陆时候动态获取的,我在登陆成功拿到菜单数据之后format了两次,有个问题是:第二层级是不能跳转的😂,感觉跟楼上的思路都差不多)

@tyxcss3
Copy link

tyxcss3 commented Mar 30, 2020

@bluegek 能看下你的具体解决方法吗

@rexerwang
Copy link

原本keep-alive是按照组件名缓存。
如果仅作用于页面组件时,结合vue-router,可改为按照当前路由路径(route.path)做缓存;Fork一份keep-alive.js,将相关部分做修改。但是可能对动态路由无法支持。

@xlb
Copy link

xlb commented Aug 10, 2020

我这边将菜单显示跟路由做了分离,目前使用下来没什么问题。

`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
}`

@geekLeopoldFitz
Copy link

geekLeopoldFitz commented Oct 26, 2020

image
image
image

二级菜单那一层的router-view中再加个keep-alive,同时设置二级菜单的组件name

@zhoumingquan
Copy link

这个问题已经解决,思路跟楼上的一样,核心就是给菜单展示和真正的路由区分开来,真正的路由最多到二级公用一个router-view,就能实现缓存了

@ixre
Copy link

ixre commented Jan 12, 2021

// 扁平化路由,在添加路由方法`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);

@sand1018
Copy link

// 扁平化路由,在添加路由方法`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);

完美解决问题

@Leslie1900
Copy link

// 扁平化路由,在添加路由方法`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);

完美解决问题
这些处理加在什么地方?

@wanqianjin
Copy link

对于多级路由嵌套使keep-alive失效的情况,在src下面的permission.js文件中加入下面代码:
router.beforeResolve((to, from, next) => {
if (to.matched && to.matched.length > 2) {
to.matched.splice(1, to.matched.length - 2)
}
next()
})
这样做就相当于让matched匹配的路由始终都是2个,tags-view导航就能缓存所有打开的页面了。
这样做会影响到面包屑导航:
下面是给出一个解决方案
1.recursiveTreeByLastLevel方法:
/**

筛选出面包屑导航的路由
筛选出最后一个路由的整个树形结构
自定义查找字段, 根据最后一级某个字段查找完整树(整个父类)
@param {} val 要查找对比的值
@param {
} data 要查找的数据
@param {*} fKey 要查找对比的字段
*/
export 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;
}
2.使用这个方法去筛选出想要的路由。
// 筛选出面包屑导航的路由(在src下面的permission.js文件中)
router.afterEach((to, from, next) => {
var routerList = recursiveTreeByLastLevel(to.name, store.state.permission.routes, 'name')
store.commit('permission/setBreadcrumbList', routerList) // 经过vuex缓存
})
3.在store下面的permission.js加入如下代码
const mutations = {
SET_ROUTES: (state, routes) => {
// 所有角色都能访问的普通路由(login、404page) + 需要鉴权的路由
state.routes = constantRoutes.concat(routes)
},
setBreadcrumbList:(state, routerList)=> {
state.breadcrumbList = routerList
}
}
4.在面包屑的文件(‘src/components/Breadcrumb/index’)里面取用即可
getBreadcrumb() {
// only show routes with meta.title
let breadcrumbList = this.$store.state.permission.breadcrumbList
let matched = breadcrumbList.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboards', meta: { title: '首页' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},

@myshumin
Copy link

myshumin commented Dec 16, 2021 via email

1 similar comment
@myshumin
Copy link

myshumin commented May 23, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests