《Fantastic-admin 技术揭秘》系列将带你了解 Fantastic-admin 这款框架各种功能的设计与实现。通过了解这些技术细节,你不光可以更轻松地使用 Fantastic-admin 这款框架,也可以在其他项目中使用这些技术。
你可以点击 这里 查看本系列的所有文章,也欢迎你在评论区留言告诉我你感兴趣的内容,或许下一篇文章就会带你揭秘其中的奥秘。
需求分析
页面刷新是很常见的一种用户行为,比如一些表单页面,用户填写了一半数据,这时候打算重新填写,就会通过刷新页面恢复到初始状态。
但直接刷新浏览器会触发整个应用的重新加载,这显然不是我们希望看到的,最好是只刷新当前页面对应的组件。
实现方案
既然是页面组件刷新,那就可以确定下几种方案:
方案一
因为后台框架是嵌套路由的结构,所以可以在共用的父组件里,通过 v-if
来重新渲染 RouterView
组件,达到刷新当前页面的效果。
vue
<script setup lang="ts">
import { usePageStore } from '@/store/modules/page';
const { isRefresh } = usePageStore();
</script>
<template>
<RouterView v-slot="{ Component, route }">
<KeepAlive :include="keepAliveStore.list">
<component v-if="!isRefresh" :is="Component" :key="route.fullPath" />
</KeepAlive>
</RouterView>
</template>
ts
// store/modules/page.ts
export const usePageStore = defineStore('page', () => {
const isRefresh = ref(false);
function setRefresh() {
isRefresh.value = true;
nextTick(() => {
isRefresh.value = false;
});
}
return { isRefresh, setRefresh };
});
把是否刷新页面这个状态交给 store
来管理,这样就可以在需要刷新页面的时候,调用 setRefresh
方法,然后页面就会重新渲染。
但是,这个方案有个弊端,因为 RouterView
组件会搭配 KeepAlive
组件一起使用,所以重新渲染 RouterView
组件,会影响到 KeepAlive
组件的缓存效果,这显然不是我们希望看到的。
方案二
既然方案一的通过 v-if
来重新渲染组件的结果不符合预期,那就换一种思路,通过跳转到一个空白页面,再立马跳转回来,也能达到刷新当前页面的效果。
我们需要准备一个 reload 页面,并写进路由配置中:
vue
<script setup lang="ts">
const router = useRouter()
onMounted(() => {
router.go(-1)
})
</script>
<template>
<div />
</template>
ts
// 注意:reload 页面也必须是嵌套路由的结构,确保它和其他业务页面是共用同一个父组件的,这样才能保证在跳转到 reload 页面时,keep-alive 组件的缓存效果不会受到影响
{
path: '/',
component: () => import('@/layouts/index.vue'),
children: [
{
path: 'reload',
name: 'reload',
component: () => import('@/views/reload.vue'),
meta: {
title: '刷新页面',
},
},
],
}
还有最后一步,如果当前路由页面是开启页面缓存的,那么在跳转到 reload 页面时,需要手动删除下当前页面在 keep-alive
中的缓存。
以上篇《精细化控制页面缓存》文章为例,需要在 router.afterEach
中判断进入的是否为 reload 页面,如果是,则删除当前页面在 keep-alive
中的缓存。
ts
// 这段代码如果看不明白,可以先阅读上篇文章
router.afterEach((to, from) => {
const keepAliveStore = useKeepAliveStore()
if (to.fullPath !== from.fullPath) {
if (to.meta.cache) {
const componentName = to.matched.at(-1)?.components?.default.name
if (componentName) {
keepAliveStore.add(componentName)
}
else {
console.warn('该页面组件未设置组件名,会导致缓存失效,请检查')
}
}
if (from.meta.cache) {
const componentName = from.matched.at(-1)?.components?.default.name
if (componentName) {
switch (typeof from.meta.cache) {
case 'string':
if (from.meta.cache !== to.name) {
keepAliveStore.remove(componentName)
}
break
case 'object':
if (!from.meta.cache.includes(to.name as string)) {
keepAliveStore.remove(componentName)
}
break
}
if (from.meta.noCache) {
switch (typeof from.meta.noCache) {
case 'string':
if (from.meta.noCache === to.name) {
keepAliveStore.remove(componentName)
}
break
case 'object':
if (from.meta.noCache.includes(to.name as string)) {
keepAliveStore.remove(componentName)
}
break
}
}
// ↓↓↓ 在这里进行处理 ↓↓↓
// 如果进入的是 reload 页面,则将离开页面的缓存清空
if (to.name === 'reload') {
keepAliveStore.remove(componentName)
}
}
}
}
})
这会,已经能基本满足页面刷新的需求了。
但这个方案也并非完美,因为毕竟是跳转到一个空白页面再跳回来,即便关闭了页面切换动画,在视觉上还是会看到页面闪烁了一下。
但话又说回来,这个闪烁的效果,或许也能作为用户反馈的一种形式,让用户知道页面刷新了。