store
javascript
import { defineStore } from 'pinia'
export const useCachedViewsStore = defineStore('cachedViews', {
state: () => ({
// 存储的是组件的 name,不是路由 path
cachedViews: [] as string[]
}),
actions: {
/** 添加缓存(路由守卫中调用) */
addCachedView(name: string) {
if (name && !this.cachedViews.includes(name)) {
this.cachedViews.push(name)
}
},
/** 移除指定缓存(关闭标签页/编辑后返回时调用) */
removeCachedView(name: string) {
const index = this.cachedViews.indexOf(name)
if (index > -1) {
this.cachedViews.splice(index, 1)
}
},
/** 清空所有缓存(退出登录时调用) */
clearAllCachedViews() {
this.cachedViews = []
}
}
})
App.vue
javascript
<template>
<router-view v-slot="{ Component, route }">
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.fullPath"/>
</keep-alive>
</router-view>
</template>
<script setup lang="ts">
import {computed} from 'vue'
import {useCachedViewsStore} from '@/store'
const cachedViewsStore = useCachedViewsStore()
const cachedViews = computed(() => cachedViewsStore.cachedViews)
</script>
router
javascript
import {createRouter, createWebHashHistory} from 'vue-router'
import routes from './routes'
import {useCachedViewsStore} from '@/store'
const asmnPosition = {}
const router = createRouter({
history: createWebHashHistory(`/#/${import.meta.env.VITE_PROJECT_NAME}/`),
routes,
scrollBehavior(to, from, savedPosition) {
if (needKeepAlive(to)) {
// savedPosition 是浏览器前进/后退时记录的位置
if (savedPosition) {
return savedPosition
}
// 代码 push 跳转,读取 asmnPosition 中保存的位置
const top = asmnPosition[to.name]
if (top)
return {top}
}
// 滚动到顶部
return {top: 0}
}
})
router.beforeEach((to, from, next) => {
const cachedViewsStore = useCachedViewsStore()
if (needKeepAlive(to))
// 注意:这里传入的是路由 name,必须和组件 name 保持一致!
cachedViewsStore.addCachedView(to.name)
if (needKeepAlive(from))
asmnPosition[from.name] = window.scrollY
next()
})
function needKeepAlive(to) {
return to.meta.keepAlive && to.name
}
export default router
router
javascript
const routes = [
{
path: '/',
// keepAlive时name必须和组件 name 保持一致!
name: 'home',
component: () => import( '@/views/Home.vue'),
meta: {
keepAlive: true,
}
},
{
path: '/test',
name: 'test',
component: () => import( '@/views/Test.vue'),
},
]
export default routes
home.vue
javascript
<template>
<div class="flex-center">
<div class="a">
<div class="b">slave-home</div>
</div>
<router-link to="/test">测试页</router-link>
<div v-for="(v, i) in 100">
{{v}}
</div>
</div>
<HomeTabbar/>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import HomeTabbar from '@/components/HomeTabbar/Index.vue'
// 定义当前组件的name,同vue2的name
defineOptions({
name: 'home'
})
const active = ref('0')
console.log('asmn')
</script>
<style lang="scss" scoped>
.a {
.b {
color: red;
}
}
</style>
通过来回切换页面 Home --> Test ---> Home,看有无日志"asmn"输出,判断keepAlive是否生效,相当于有输出代表生效,即页面内的组件在test -> push -> Home回来的时候没被销毁。
keepAlive无法记录滚动条,所以也单独处理了,可观察切换回来时,滚动条是否与上次一致。
注意:使用keepAlive与记录滚动条,router里面的name需要与组件的name保持一致