【推荐】微前端MicroApp中页面级缓存控制如何实现?

微前端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变为,然后跳转到其他页面再回来还是有缓存

期望:当前路由在其他应用时,删除标签页重新打开 缓存被重置

看完忍不住感叹,楼主真的太用心了,整理这么详细的内容肯定花了不少时间和精力,不仅知识点全面,还搭配了清晰的逻辑梳理,让人一看就懂,这样的优质内容必须点赞收藏,也希望楼主能一直坚持分享下去!