解决方案在下面, 可以直接跳过, 这是我遇到的一些问题
先说一下问题所在,虽然vue3.0 不需要 root div, 但是
keep-alive
transition
这两个标签都需要
错误示范
root div
不能加在 component 外层
<transition v-if="settings.mainNeedAnimation" name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<div>
<component :is="Component" :key="key" />
</div>
</keep-alive>
</transition>
正确使用
路由组件使用
<router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
路由页面代码(记住你的单个页面一定要使用一个 root div
),例如下面是一个.vue 文件
<template>
<div>
<div></div>
<div></div>
<div></div>
xxxxxxxxxxxxxxx
</div>
</template>
如果你是多级路由, 每一个 router-view
内部都是需要 keep-alive
包裹, keep-alive
内部不能有任何判断条件, 要简约到只有上面例子的代码
这里是我遇到的问题
后文:
醉了, 贴个文章链接都不让, 真踏马的闭关锁国
vue element admin 中所遇到的问题
路由结构, 这里所有的父级路由都已经使用 keepalive
{
path: '/metadata',
component: View,
name: 'Meta',
// redirect: '/metadata/caliber',
meta: {
title: '元数据',
skipShow: true,
},
children: [
{
path: 'caliber',
name: 'Meta_Caliber',
// redirect: '/metadata/caliber/dimension',
component: LayoutVue,
meta: {
title: '口径管理',
icon: 'sidebar-manage',
alwaysShow: true,
},
children: [
{
path: 'test',
name: 'Test',
component: () => import('@/views/test1/index.vue'),
meta: {
title: '测试1',
},
children: [{
path: 'subTest',
name: 'TestSub',
component: () => import('@/views/test1/TestSub.vue'),
meta: {
title: '测试Sub',
},
}],
},
{
path: 'test2',
name: 'Test2',
component: () => import('@/views/test2/index.vue'),
meta: {
title: '测试2',
},
children: [{
path: 'subTest2',
name: 'TestSub2',
component: () => import('@/views/test2/TestSub2.vue'),
meta: {
title: '测试Sub2',
},
}],
},
}
这 devtools 里面可以看到 TestSub2 组件不正常的归属于两个父级组件, 一个处于没有激活的 Test , 一个是已经激活的 Test2
下面是我测试的 mounted 打印, 这里的 TestSub2 mounted 生命周期被重复调用2次
test2 代码
<script setup lang="ts">
const count = ref(0)
onMounted(() => {
console.log('Test2');
})
</script>
<script lang="ts">
export default {
name: 'Test2',
}
</script>
<template>
<div class="">
Test2
<div @click="count++">
{{ count }}
</div>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</div>
</template>
<style lang="scss"></style>
TestSub2 代码
<script setup lang="ts">
const count = ref(0)
onMounted(() => {
console.log('TestSub2');
})
</script>
<template>
<div class="">
TestSub2
<div @click="count++">
{{ count }}
</div>
</div>
</template>
<style lang="scss"></style>
当我多次点击菜单后, Test, Test2 两个组件都已经拥有了两个子组件, 在这里不知道为什么, 然后因为两个组件都已经被缓存, 此时的 mounted 都不会被调用了
解决方案
两个父级组件共同一个组件
这里只会有一个组件, 所以 mounted 只会打印一次
点击另外一个路由后的效果, 很正常
在 vue element admin 代码里面的多级路由存在一个问题, tagview
组件中一个往 cacheview 添加组件name的方法
const addTags = () => {
if (route?.name && !route.meta.noCache) {
tagsViewStore.addVisitedView({ ...route })
}
return false
}
当一个路由拥有 redirect 属性的时候, 那么这个方法将无法把他的name 添加到 cacheview 数组里面, 而是把他重定向的子级name添加进去了。因为keepalive 是对直接自己组件生效的, 那么缓存就会失效, 可以创建一个 Test
组件(名字随便取,但是需要的keepalive
的数组中注意一定要准确, 他可以是公用的, 所有的同级路由都可以用它来当做 Routeview
页面), 用它来占位;然后在 AppMain
的 keepalive
里面提前写上这个组件的 name
, 此时你可能会考虑到怎么解决当我关闭 tagview 的标签时怎么关闭缓存, 你可以在 Test 组件的 keepalive
上关联到 cacheviews
, 当你关闭tag时, 这个路由的name
就被删除了, 也就是说缓存的关键是 Test 这个组件的keepalive
的数组中存在哪些name, 就算是 Test 一直被缓存, 但是他可以控制谁会被自己缓存
AppMain 代码
Test 代码
社区的解决方案
把所有超过二级的路由铺平为二级路由
vue vben 项目用的就是这个方案, 感兴趣的可以去看看