同时使用transition+keep-alive时二级路由之间跳转会导致页面组件重复渲染

本来想测试一下vue3 + vue-router4对嵌套路由的路由复用的支持情况的,结果碰到了奇怪的BUG

前提条件:vue-router: @4.0.12,试过@4.0.3BETA并未复现,并且我的这个写法在@4.0.3BETA中是不支持的

有A、B两个菜单,这两个菜单底下分别各有两个页面:MenuAChild1、MenuAChild2、MenuBChild1 和 MenuBChild2,A和B是一级路由,其底下的页面是二级路由

在同时使用transition+keep-alive时,同属于一个菜单的页面之间跳转没有问题,不同菜单的页面之间跳转会导致页面组件重复渲染

这是我的目录结构
image

这是我的App.vue

	<template>
	<div>菜单A:</div>
	<div>
		<router-link to="/MenuAChild1">菜单A页面1</router-link>
	</div>
	<div>
		<router-link to="/MenuAChild2">菜单A页面2</router-link>
	</div>
	<div>菜单B:</div>
	<div>
		<router-link to="/MenuBChild1">菜单B页面1</router-link>
	</div>
	<div>
		<router-link to="/MenuBChild2">菜单B页面2</router-link>
	</div>
	<div style="margin-top:1em;">
		<router-view v-slot="{ Component }">
			<transition name="mainRouter">
				<keep-alive>
					<component :is="Component" />
				</keep-alive>
			</transition>
		</router-view>
	</div>
</template>

这是我的菜单,A、B菜单内容相同

<template>
	<router-view v-slot="{ Component }">
		<keep-alive>
			<component :is="Component" />
		</keep-alive>
	</router-view>
</template>

这是我的页面,四个页面内容相同

<template>
	<div class="base-content" key="MenuAChild1">MenuAChild1</div>
</template>

这是我的router.js

import { createRouter, createWebHistory, RouterView } from 'vue-router'
const routerHistory = createWebHistory()
const routes = [{
	path: '/',
	name: 'Home',
	meta: {
		title: '首页',
	},
	component: () => import('../views/Home.vue')

}, {
	path: '/MenuA',
	name: 'MenuA',
	meta: {
		title: '菜单A',
	},
	component: () => import('../views/MenuA/index.vue'),
	children: [{
		path: '/MenuAChild1',
		name: 'MenuAChild1',
		meta: {
			title: '菜单A页面1',
		},
		component: () => import('../views/MenuA/MenuAChild1.vue')

	}, {
		path: '/MenuAChild2',
		name: 'MenuAChild2',
		meta: {
			title: '菜单A页面2',
		},
		component: () => import('../views/MenuA/MenuAChild2.vue')

	}]
}, {
	path: '/MenuB',
	name: 'MenuB',
	meta: {
		title: '菜单B',
	},
	component: () => import('../views/MenuB/index.vue'),
	children: [{
		path: '/MenuBChild1',
		name: 'MenuBChild1',
		meta: {
			title: '菜单B页面1',
		},
		component: () => import('../views/MenuB/MenuBChild1.vue')

	}, {
		path: '/MenuBChild2',
		name: 'MenuBChild2',
		meta: {
			title: '菜单B页面2',
		},
		component: () => import('../views/MenuB/MenuBChild2.vue')

	}]

}]
const router = createRouter({
	history: routerHistory,
	routes
})
export default router

已解决,在一级路由处的keep-alive标签下增加一层标签即可,二级路由处不需要修改

如下代码,我增加了一层<el-scrollbar>标签

				<router-view v-slot="{ Component }">
					<transition name="mainRouter">
						<keep-alive>
							<el-scrollbar :key="currentRoute" style="height:100%">
								<component :is="Component" />
							</el-scrollbar>
						</keep-alive>
					</transition>
				</router-view>

另,之前几天也有试过使用div替代el-scrollbar的位置,但是会导致切换路由时transition动画效果不出现,不知道为什么,实在懒得查了就这样吧。

我是由于两级嵌套路由都使用keep-alive,导致一级路由切换时,会走二级路由的离场动效然后再走一级路由的离场动效。我的的解决方案如下:
/**
* BUG现象:
* 1.点击一级菜单切换时,会走两次离场动效
* 2.二级菜单里的切换正常
* 问题出现原因分析:
* 1.router-view有两级嵌套,每一级嵌套都使用keep-alive使其缓存
* 2.二级菜单切换时,只会触发二级router-view的keep-alive,所以旧view离场动效正常
* 3.一级菜单切换时,两个router-view的keep-alive都触发,所以旧view的离场动效触发两次
* 解决方案:
* 1. 一级菜单按需使用keep-alive,当一级菜单切换时,激活旧view的keep-alive,使旧view的二级route-view正常走离场动效
* 2. 走完旧view离场动效走后,再屏蔽keep-alive,删除掉旧view
* 解决细节:
* 1.只有二级route keep-alive, 保证浮窗工具栏正常走离场动效果
* 2. 监听路由变化(使用onBeforeRouteUpdate(to, from)),当切换一级route变化时(即to.matched[1] !== from.matched[1]),暂时让当前Component(旧view)的 KeepAlive激活一下
* 3.重点!!让二级route的离场动效正常走完
* 3. 当旧二级route的离场动效走完后,就会触发nextTick,nextTick中使KeepAlive 为 false, 则会将旧一级route的view缓存删除
*/

一级路由:
image

监听路由变化:
onBeforeRouteUpdate((from, to)=> {
if (from && (to.matched.length > 2 && from.matched.length > 2) && to.matched[1] !== from.matched[1]) {
keepAlive.value = true;
nextTick(() => {
keepAlive.value = false;
});
}
});