vue 不完美的多标签页解决方案
·
多标签页多用在中后台管理系统,能够让用户同时打开多个标签页,而且不会丢失之前填写的内容,操作起来会比较方便。虽然部分开源项目有多标签页的功能,但就体验来看,算不上特别好。
目标#
- 可以通过router.push实现打开标签页
- 同一路由组件可以多开并且数据能够缓存下来
- 不需要处理是否缓存导致的生命周期不一致的问题
- 多标签页可以关闭,同时KeepAlive中的缓存清除
存在的问题#
要实现多标签页的缓存,最简单的方法就是用RouterView配合KeepAlive。
<RouterView v-slot="{ Component }">
<KeepAlive>
<component :is="Component" />
</KeepAlive>
</RouterView>
然而,这个方案存在几个问题:
- 不能重复打开同一个路由,而是原有的组件被激活
- 组件生命周期发生变化
不能重复打开路由#
如果给路由添加参数,打开第一次没有任何问题,但如果换另一个参数打开,还会是之前的页面,因为组件被缓存下来了。
例如:
新增一个路由 counter,在页面上添加RouterLink,并使用不同的参数
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/home">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
<RouterLink to="/counter?id=1">Counter 1</RouterLink>
<RouterLink to="/counter?id=2">Counter 2</RouterLink>
</nav>
</div>
</header>
<RouterView v-slot="{ Component }">
<KeepAlive>
<component :is="Component" />
</KeepAlive>
</RouterView>
</template>
然后再Counter组件中获取id参数,分别点击Counter 1和Counter 2,会发现点击Counter 1时获取到的id是1,点击Counter 2时却没有任何变化,而且两个RouterLink同时是激活状态。
组件生命周期变化#
和上一个问题有所关联,因为组件没有重新加载,在需要重新获取数据时,KeepAlive改变了组件的生命周期,添加了onActivated和onDeactivated生命周期。
添加一个组件测试生命周期:
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<script setup>
import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount, onActivated, onDeactivated } from 'vue'
onMounted(() => { console.log("onMounted") })
onUpdated(() => { console.log("onUpdated") })
onUnmounted(() => { console.log("onUnmounted") })
onBeforeMount(() => { console.log("onBeforeMount") })
onBeforeUpdate(() => { console.log("onBeforeUpdate") })
onBeforeUnmount(() => { console.log("onBeforeUnmount") })
onActivated(() => { console.log("onActivated") })
onDeactivated(() => { console.log("onDeactivated") })
</script>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>
再修改App.vue
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/home">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
<RouterLink to="/counter?id=1">Counter 1</RouterLink>
<RouterLink to="/counter?id=2">Counter 2</RouterLink>
</nav>
</div>
</header>
<RouterView v-slot="{ Component }">
<!-- <KeepAlive> -->
<component :is="Component" />
<!-- </KeepAlive> -->
</RouterView>
</template>
<script setup>
import { watch } from 'vue'
import { RouterLink, RouterView, useRoute } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
const route = useRoute()
watch(route, () => {
console.log("页面切换", route.fullPath)
})
</script>
先从Home切换到About再切换回Home再切换回About。
查看在不使用KeepAlive切换页面时候的输出,onBeforeMount -> onMounted -> onBeforeUnmount -> onUnMounted 循环
使用KeepAlive的情况,情况就复杂很多,每次切换到页面时会激活onActivated钩子,正常情况下可以通过onActivated钩子获取路由参数,重新获取数据。
更多推荐




所有评论(0)