micro-app微前端的尝试

4 阅读3分钟

micro-app微前端的尝试

背景:项目组需要把crm项目和erp项目,这两个已有的项目集成到一个项目平台上使用。所以使用了micro-app微前端作为基座,crm和erp作为子应用展示。

原理:使用类webcomponent组件把各个子应用集成起来,类似于在基座应用中嵌套iframe页面,使用micro-app暴露出来的方法通讯。可以把不同技术的子应用放到同一个基座中展示。

项目中遇到的问题: 1、子应用、基座和控制台window指向问题。 子应用的上下文和基座的是互相隔离开的,基座和子应用有各自的window挂载不同的数据,控制台中的window是基座的上下文获取的。想要子应用的window只能在代码中log查看

2、子应用中触发路由跳转的实现 一开始用的是``` microApp.router.push


原因:因为从子应用A跳转到子应用b,需要先把子应用a卸载后,挂载子应用B。这个过程是异步的,当执行microApp.router.push跳转是同步的,就会拿不到对应跳转的子应用。

最终实现方法:通过轮询去判断是否已经挂在了目标应用,如果过载了再执行跳转

/**
 * 安全的子应用跳转方法
 * @description 检查子应用状态后再进行跳转,避免"子应用没有挂载导航失败"的错误
 * @param {string} appName - 子应用名称
 * @param {string} path - 跳转路径
 * @returns {Promise<void>}
 */
async function safeMicroAppNavigation(appName: string, path: string) {
  const activeApps = microApp.getActiveApps({
    excludeHiddenApp: true,
    excludePreRender: true,
  });

  // 如果子应用已激活且已初始化,直接跳转
  if (activeApps?.includes(appName) && isAppInitialized(appName)) {
    try {
      microApp.router.push({ name: appName, path });
      console.log(`✅ 子应用 ${appName} 已激活,直接跳转到 ${path}`);
      return;
    } catch (error) {
      console.error('microApp.router.push 失败,回退到 router.push:', error);
      router.push({ path });
      return;
    }
  }

  // 子应用未激活,显示加载状态并开始轮询
  console.log(`⏳ 子应用 ${appName} 未激活,开始轮询等待...`);
  loadingStore.showGlobalLoading('正在跳转页面...');

  // 清除之前的定时器
  if (microAppActiveCheckTimer !== null) {
    window.clearInterval(microAppActiveCheckTimer);
    microAppActiveCheckTimer = null;
  }

  // 开始轮询检查子应用状态
  microAppActiveCheckTimer = window.setInterval(() => {
    const apps = microApp.getActiveApps({
      excludeHiddenApp: true,
      excludePreRender: true,
    });

    console.log(
      `🔍 轮询检查: 子应用 ${appName} 是否激活`,
      apps?.includes(appName),
    );

    if (apps?.includes(appName) && isAppInitialized(appName)) {
      try {
        microApp.router.push({ name: appName, path });
        console.log(`✅ 子应用 ${appName} 已激活,跳转到 ${path}`);

        // 清除定时器
        if (microAppActiveCheckTimer !== null) {
          window.clearInterval(microAppActiveCheckTimer);
          microAppActiveCheckTimer = null;
        }

        // 延迟隐藏加载状态,确保子应用路由跳转完成
        setTimeout(() => {
          loadingStore.forceHideGlobalLoading();
        }, 1000);
      } catch (error) {
        console.error('microApp.router.push 失败,回退到 router.push:', error);
        router.push({ path });
        loadingStore.forceHideGlobalLoading();
      }
    }
  }, 300);

  // 设置超时机制,避免无限轮询
  setTimeout(() => {
    if (microAppActiveCheckTimer !== null) {
      console.warn(`⚠️ 子应用 ${appName} 激活超时,回退到 router.push`);
      window.clearInterval(microAppActiveCheckTimer);
      microAppActiveCheckTimer = null;
      loadingStore.forceHideGlobalLoading();
      router.push({ path });
    }
  }, 10000); // 10秒超时
}

基座还支持向子应用注册路由对象,在子应用中进行跳转。为了以后维护性好一些,还是把所有的跳转放在基座处理(建议)




总结:micro-app可以理解成多项目管理工具,它同样具备属于自己的生命周期、环境变量、和路由管理方式。通过样式隔离和元素隔离加scope区分不同的项目样式,当然这些都是可配置的。还提供了不同应用之间的通讯方式。也可以在基座上层给各个子应用增加插件例如高德地图。当套的子应用不断增加,micro-app加载的速度也会有所变慢,ma提供了预加载方案。