微前端MicroApp中页面级缓存控制如何实现?
架构:主应用和子应用都是vue3+vite,微前端框架是京东的MicroApp
是通过keep-alive的include实现的缓存控制,
目前出现问题:同一个子应用功能修改数据后,点击子应用的关闭修改了缓存传递的数据,再次进入删除标签的功能页,缓存清除没问题,但是如果我当前是在其他应用的页面,此时删除标签,在点击该删除的页面还是修改后的数据
<template>
<div :id="containerId">
<micro-app
v-bind="options"
:name="name"
:url="url"
:baseroute="baseRoute"
:data="microAppData"
@created="handleCreated"
@beforemount="handleBeforeMount"
@mounted="handleMounted"
@unmount="handleUnmount"
@error="handleError"
></micro-app>
</div>
</template>
<script setup lang="ts">
import { computed, onActivated, ref, onDeactivated } from 'vue'
import { useUserStore } from '@/store/user'
import { useTagsViewStore } from '@/store/tags-view'
import { useRoute } from 'vue-router'
const props = defineProps<{
name: string
url: string
pathPrefix?: string
keepAlive?: boolean
iframe?: boolean
}>()
const userStore = useUserStore()
const tagsViewStore = useTagsViewStore()
const route = useRoute()
// 引入状态锁,确保仅在 MicroApp 挂载后操作路由
const isAppMounted = ref(false)
// 记录激活时间,强制驱动数据更新
const activatedTime = ref(Date.now())
// 明确下发应用可见性状态
const isVisible = ref(true)
const containerId = computed(() => `micro-app-${props.name}`)
/* 配置基座路由前缀 默认是/name/ */
const baseRoute = computed(() => props.pathPrefix || `/${props.name}/`)
const options = computed(() => ({
'disable-memory-router': true,
'keep-alive': props.keepAlive ?? false,
iframe: props.iframe ?? false
}))
// 通过 data 属性下发路由路径,让子应用自行监听并跳转
const microAppData = computed(() => {
const cacheItems = tagsViewStore.getAppCache(props.name) || []
// 计算需要缓存的路径(去除基座前缀)
const keepAlivePaths = tagsViewStore.getAppCacheNames(props.name)
return {
user: userStore.userInfo,
token: userStore.token,
path: route.fullPath, // 实时同步当前完整路径
pushState: Date.now(), // 强制触发更新的时间戳
activatedTime: activatedTime.value, // 激活时间戳,确保切换Tab时触发更新
isAppVisible: isVisible.value, // 明确告知子应用当前的可见性
cacheItems: cacheItems, // 保留原始数据以备不时之需
keepAlivePaths: keepAlivePaths, // 处理后的路径列表
keepAliveExclude: []
}
})
onActivated(() => {
activatedTime.value = Date.now()
isVisible.value = true
})
onDeactivated(() => {
isVisible.value = false
})
const handleCreated = () => {}
const handleBeforeMount = () => {}
const handleMounted = () => {
isAppMounted.value = true
}
const handleUnmount = () => {
isAppMounted.value = false
}
const handleError = () => console.error(`[MicroApp] ${props.name} error`)
</script>
<template>
<router-view v-slot="{ Component, route }">
<keep-alive :include="aliveInclude">
<component :is="formatComponent(Component, route)" />
</keep-alive>
</router-view>
</template>
const syncKeepAliveState = (data: any) => {
console.log('data.keepAlivePaths', data.keepAlivePaths)
if (!data?.keepAlivePaths) return
alivePaths.value = data.keepAlivePaths
console.log(' syncKeepAliveState', data.keepAlivePaths)
// 遍历当前 aliveInclude,移除那些不在 alivePaths 中的组件
const newInclude = aliveInclude.value.filter(name => {
// 1. 尝试找到该组件名对应的路径
const path = [...pathNameMap.entries()].find(([k, v]) => v === name)?.[0]
// 2. 如果找不到路径(可能是异常情况),默认保留
if (!path) return true
// 3. 检查该路径是否仍在允许缓存的列表中
return alivePaths.value.includes(path)
})
if (newInclude.length !== aliveInclude.value.length) {
aliveInclude.value = newInclude
}
}
const handleDataChange = (data: any) => {
// 0. 同步应用可见性 (由基座明确下发)
if (typeof data.isAppVisible !== 'undefined') {
isAppVisible.value = data.isAppVisible
}
// 1. 实时同步缓存状态(主应用推送)
// 只要收到数据就尝试同步,确保后台也能清理缓存
syncKeepAliveState(data)
...
}
onMounted(() => {
// 监听来自主应用的数据变化
window.microApp?.addDataListener(handleDataChange, true) // true 表示立即触发一次回调
})
onUnmounted(() => {
window.microApp?.removeDataListener(handleDataChange)
})
/* 缓存 */
const setAppCache = (appName: string, item: TagItem[]) => {
cachedApps.value.set(appName, item)
// 向子应用实时推送(若子应用处于激活状态则可接收)
if (appName !== 'main') {
MicroAppUtils.updateKeepAlivePaths(appName, getAppCacheNames(appName))
}
}
/* 获取缓存 */
const getAppCache = (appName: string) => {
return cachedApps.value.get(appName) || []
}
/* 获取缓存路由name列表 */
const getAppCacheNames = (appName: string) => {
return getAppCache(appName)
.filter(item => item.keepAlive !== false)
.map(item => {
return removeStartBasePath(item.fullPath, appName)
})
}
/* 移除缓存 */
const removeAppCache = (appName: string, name: string) => {
const data = getAppCache(appName).filter(v => v.name !== name)
setAppCache(appName, data)
}
尝试:通过microAppData自动下方缓存数据,但是在其他路由页面的时候子应用没有执行handleDataChange,我限制了路由跳转 删除不跳转,手动跳转执行了handleDataChange,aliveInclude变为,然后跳转到其他页面再回来还是有缓存
期望:当前路由在其他应用时,删除标签页重新打开 缓存被重置