微前端路由设计方案 & 子应用管理保活

版本: v2.0(sync 模式重构)
日期: 2026-02-09
框架: wujie(无界)— alive + sync 模式
适用范围: CMCLink 微前端主应用 + 7 个子应用


一、架构总览

1.1 系统拓扑

┌─────────────────────────────────────────────────────────────────┐
│                    CMCLink 微前端架构                             │
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              主应用 @cmclink/main (:3000)                  │  │
│  │  ┌─────────┐  ┌──────────────┐  ┌─────────────────────┐  │  │
│  │  │ Router  │  │ AuthLayout   │  │     App.vue         │  │  │
│  │  │ (Vue)   │  │ (Header/Tab) │  │ (WujieVue 容器)     │  │  │
│  │  └────┬────┘  └──────┬───────┘  └──────────┬──────────┘  │  │
│  │       │              │                      │             │  │
│  │       │     wujie bus (EventEmitter)        │             │  │
│  │       │    ┌─────────┴──────────┐           │             │  │
│  └───────┼────┼────────────────────┼───────────┼─────────────┘  │
│          │    │                    │           │                 │
│  ┌───────┴────┴────────────────────┴───────────┴─────────────┐  │
│  │                    子应用沙箱层 (wujie)                      │  │
│  │                                                           │  │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────────┐   │  │
│  │  │  doc    │ │  mkt    │ │ common  │ │ ibs-manage   │   │  │
│  │  │ :3003   │ │ :3001   │ │ :3006   │ │    :3007     │   │  │
│  │  └─────────┘ └─────────┘ └─────────┘ └──────────────┘   │  │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐                    │  │
│  │  │commerce │ │operation│ │ general │                    │  │
│  │  │-finance │ │ :3004   │ │ :3005   │                    │  │
│  │  │ :3002   │ │         │ │         │                    │  │
│  │  └─────────┘ └─────────┘ └─────────┘                    │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              共享包 @cmclink/micro-bridge                   │  │
│  │  registry.ts │ url.ts │ types.ts │ bridges                │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

1.2 子应用注册表

所有子应用在 packages/micro-bridge/src/registry.ts 中统一注册:

子应用名称端口activeRuleentry状态
mkt3001/mkt/mkt/待迁移
commerce-finance3002/commerce-finance/commerce-finance/待迁移
doc3003/doc/doc/✅ 已迁移
operation3004/operation/operation/待迁移
general3005/general/general/待迁移
common3006/common/common/待迁移
ibs-manage3007/ibs-manage/ibs-manage/✅ 已迁移

1.3 Monorepo 项目结构

微前端/
├── apps/
│   ├── cmclink-web-micro-main/   # 主应用 @cmclink/main
│   ├── doc/                       # 子应用 @cmclink/doc
│   └── ibs-manage/                # 子应用 @cmclink/ibs-manage
├── packages/
│   ├── micro-bridge/              # 微前端通信 SDK
│   ├── micro-bootstrap/           # 子应用启动器
│   ├── tsconfig/                  # 共享 TS 配置
│   └── vite-config/               # 共享 Vite 配置
├── pnpm-workspace.yaml
└── turbo.json

二、路由设计方案

2.1 URL 设计原则

核心原则:URL 对用户无感,子应用路径直接拼接在主应用路径后。

主应用 base:  /micro-main/
子应用路由:   /micro-main/{appName}/{子应用内部路径}

示例:
  /micro-main/                                          → 主应用首页
  /micro-main/ibs-manage/operation/enterpriseMgmt       → ibs-manage 子应用
  /micro-main/doc/document/blManage                     → doc 子应用
  /micro-main/profile                                   → 主应用个人中心

2.2 主应用路由配置

主应用 Vue Router 采用 createWebHistory(VITE_BASE_PATH) 模式,路由分为三层:

路由树:
├── /login                          # 公开路由(无需登录)
├── /forget-password                # 公开路由
├── /resetPwd                       # 公开路由
├── /                               # AuthenticatedLayout(需登录)
│   ├── /                           # 主应用首页
│   ├── /profile                    # 个人中心
│   ├── /message-center             # 消息中心
│   ├── /mkt/:pathMatch(.*)*        # 子应用占位路由(render null)
│   ├── /commerce-finance/:pathMatch(.*)*
│   ├── /doc/:pathMatch(.*)*
│   ├── /ibs-manage/:pathMatch(.*)*
│   ├── /common/:pathMatch(.*)*
│   ├── /operation/:pathMatch(.*)*
│   └── /general/:pathMatch(.*)*
└── /:pathMatch(.*)*                # 404 兜底

关键设计点

  • 子应用路由使用 /:pathMatch(.*)* 通配符,确保 /ibs-manage/operation/xxx 等深层路径都能匹配
  • 子应用路由的 component 设为 { render: () => null },不渲染任何主应用组件
  • 子应用路由的 meta.appName 标识所属子应用,用于 Tab 管理
  • 所有子应用路由都是 AuthenticatedLayout 的 children,确保 Header/Tab 始终显示

2.3 主应用显隐控制

App.vueAuthenticatedLayout.vue 协同控制主应用内容与子应用容器的显隐:

App.vue:
├── <RouterView>                    # 始终渲染(AuthenticatedLayout 被 keep-alive 缓存)
│   └── <AuthenticatedLayout>
│       ├── <LayoutHeader>          # 始终显示
│       ├── <SiderMenu>             # 始终显示
│       └── <main-app-content>      # v-show="!findAppByRoute(route.path)"
│           └── <RouterView>        # 主应用页面
│
└── <child-app-container>           # v-if="userStore.isSetUser && isChildRoute"
    └── <WujieVue v-for>            # v-show="route.path.startsWith(app.activeRule)"
        ├── doc
        ├── ibs-manage
        └── ...

显隐判断逻辑

场景main-app-contentchild-app-container说明
/ (首页)✅ 显示❌ 隐藏findAppByRoute 返回 undefined
/profile✅ 显示❌ 隐藏主应用页面
/ibs-manage/xxx❌ 隐藏✅ 显示子应用路由,前缀匹配
/doc/xxx❌ 隐藏✅ 显示子应用路由

2.4 子应用 URL 生成

开发环境和生产环境使用不同的 URL 生成策略:

// packages/micro-bridge/src/url.ts
function getAppUrl(app: MicroAppConfig, envGetter): string {
  // 环境变量 key: VITE_${APP_NAME}_APP_URL
  // 例如: VITE_IBS_MANAGE_APP_URL=http://localhost:3007
  const envKey = `VITE_${app.name.toUpperCase().replace(/-/g, '_')}_APP_URL`
  const envUrl = envGetter(envKey)

  if (envUrl) {
    // 开发环境: http://localhost:3007 + /ibs-manage/ = http://localhost:3007/ibs-manage/
    return `${envUrl}${app.entry}`
  }
  // 生产环境: 直接使用 entry(相对路径,由 nginx 反向代理)
  return app.entry
}

环境变量配置.env.base):

VITE_MKT_APP_URL=http://localhost:3001
VITE_COMMERCE_FINANCE_APP_URL=http://localhost:3002
VITE_DOC_APP_URL=http://localhost:3003
VITE_OPERATION_APP_URL=http://localhost:3004
VITE_GENERAL_APP_URL=http://localhost:3005
VITE_COMMON_APP_URL=http://localhost:3006
VITE_IBS_MANAGE_APP_URL=http://localhost:3007

三、路由同步机制

3.1 wujie sync 模式

采用 wujie 官方推荐的 sync 模式,由框架自动完成主应用与子应用之间的路由同步,无需手动 bus 通信、无需防循环标记

<!-- App.vue -->
<WujieVue
  :name="app.name"
  :url="getAppUrl(app)"
  :alive="true"
  :sync="true"    ← 启用 sync 模式
/>

sync 模式原理

  • wujie 内部劫持子应用的 history.pushState / history.replaceState
  • 子应用路由变化时,wujie 自动将子应用路径同步到主应用 URL
  • 主应用 URL 变化时,wujie 自动驱动子应用路由跳转
  • 整个过程由框架内部处理,无循环风险

3.2 数据流设计

设计原则:路由同步完全委托给 wujie sync 模式,业务层只负责 router.push

┌─────────────────────────────────────────────────────────────┐
│                     路由同步数据流                             │
│                                                             │
│  场景 A: 菜单/Tab 点击                                       │
│  ──────────────────────                                     │
│  menuClick / tabClick                                       │
│    └── router.push('/ibs-manage/operation/xxx')             │
│         │                                                   │
│         ▼                                                   │
│  主应用 URL 变化 → wujie sync 自动驱动子应用路由跳转           │
│    └── 子应用 router 自动跳转到 /operation/xxx               │
│                                                             │
│  ✅ 无需 bus 通信,无循环风险                                  │
│                                                             │
│                                                             │
│  场景 B: 子应用内部导航                                       │
│  ──────────────────────                                     │
│  子应用内部点击链接 → router.push('/operation/yyy')           │
│         │                                                   │
│         ▼                                                   │
│  wujie sync 自动同步到主应用 URL                              │
│    └── 主应用地址栏更新为 /ibs-manage/operation/yyy           │
│                                                             │
│  ✅ 无需 bus 通信,无循环风险                                  │
└─────────────────────────────────────────────────────────────┘

3.3 与手动方案对比

维度sync 模式(当前方案)手动 bus 双向通信(旧方案)
路由同步框架自动处理手动 bus.$emit + bus.$on
防循环框架内部处理,无风险_fromMainApp 标记,存在竞态风险
代码量零额外代码setupRouterSync + updateQuery + afterEach
子应用改造无需任何路由同步代码每个子应用需实现 setupRouterSync
可维护性高(依赖框架标准能力)低(自定义逻辑,排查困难)

3.4 路径规范化

menuClick 中对路径进行规范化,兼容不同来源的路径格式:

// tabs.ts → menuClick
const prefix = `/${tab.appName}`;
let fullPath = tab.path || prefix;
// TO_ROUTE 传来的可能是子应用内部路径(如 /operation/xxx),需要补前缀
if (!fullPath.startsWith(prefix)) {
  fullPath = `${prefix}${fullPath.startsWith("/") ? "" : "/"}${fullPath}`;
}
// wujie sync 模式会自动同步子应用路由,只需 push 即可
router.push(fullPath);

四、通信事件协议

4.1 事件总线

使用 wujie 内置的 bus(基于 EventEmitter),所有子应用共享同一个 bus 实例。

4.2 事件清单

注意:路由同步已由 wujie sync 模式自动处理,bus 事件仅用于业务通信

子应用 → 主应用

事件名触发时机数据结构处理方
TO_ROUTE子应用请求跨应用跳转{ appName, path, query, name }AuthenticatedLayoutmenuClick
ASSETS_404子应用静态资源加载失败{ appName }AuthenticatedLayout → 弹窗提示刷新
CLOSE_ALL_TABS子应用请求关闭所有 Tab{ appName }AuthenticatedLayoutremoveTab

主应用 → 子应用

事件名触发时机数据结构处理方
CLOSE_ALL_TAB_TO_CHILD关闭子应用 Tab{ appName }子应用监听 → 重置状态
REFRESH_CHILD刷新子应用{ appName }子应用监听 → 重新加载当前路由

已废弃事件(由 sync 模式替代)

事件名废弃原因
ROUTE_CHANGE子→主路由同步已由 sync 模式自动处理
ROUTER_CHANGE_TO_CHILD主→子路由同步已由 sync 模式自动处理

4.3 事件使用原则

  • 路由同步:完全依赖 wujie sync 模式,禁止通过 bus 手动同步路由
  • 业务通信:跨应用跳转(TO_ROUTE)、资源异常(ASSETS_404)等业务场景仍使用 bus
  • 事件过滤:子应用通过 data.appName 过滤非自身事件

五、子应用管理与保活方案

5.1 wujie alive 模式

所有子应用均使用 alive 保活模式

<!-- App.vue -->
<WujieVue
  v-for="app in loadedApps"
  :key="app.name"
  :alive="true"           ← 保活模式
  v-show="route.path.startsWith(app.activeRule)"
  :name="app.name"
  :url="getAppUrl(app)"
  :props="{ token, userInfo }"
/>

alive 模式特性

  • 子应用首次加载后,实例不销毁,切换时仅做 display: none
  • 子应用的 Vue 实例、Pinia Store、DOM 状态全部保留
  • 切换回来时无需重新初始化,体验接近原生 Tab 切换
  • 子应用内部的表单填写、滚动位置、弹窗状态等全部保留

5.2 按需渲染策略

为避免未启动的子应用触发加载错误,采用按需渲染策略:

// App.vue
const visitedApps = reactive(new Set<string>())

// 仅渲染用户已访问过的子应用
const loadedApps = computed(() =>
  microAppRegistry.filter((app) => visitedApps.has(app.name))
)

// 监听路由变化,标记已访问
watch(() => route.path, (path) => {
  const matched = findAppByRoute(path)
  if (matched) {
    visitedApps.add(matched.name)
  }
}, { immediate: true })

生命周期

用户首次访问 /ibs-manage/xxx
  → visitedApps.add('ibs-manage')
  → loadedApps 包含 ibs-manage
  → WujieVue 组件渲染 → 加载子应用 → 挂载
  → v-show=true(当前激活)

用户切换到 /doc/xxx
  → visitedApps.add('doc')
  → ibs-manage: v-show=false(隐藏但保活)
  → doc: WujieVue 渲染 → 加载 → v-show=true

用户切回 /ibs-manage/yyy
  → ibs-manage: v-show=true(瞬间恢复,无需重新加载)
  → doc: v-show=false(隐藏但保活)

5.3 预加载策略

AuthenticatedLayoutonMounted 中触发预加载,分优先级:

// plugins/wujie.ts
export function preloadChildApps() {
  const highPriority = ['doc', 'mkt']  // 高频子应用

  // 高优先级:立即预加载
  highPriorityApps.forEach(app => {
    preloadApp({ name: app.name, url: getAppUrl(app) })
  })

  // 低优先级:延迟 3 秒后预加载
  setTimeout(() => {
    lowPriorityApps.forEach(app => {
      preloadApp({ name: app.name, url: getAppUrl(app) })
    })
  }, 3000)
}

预加载 vs 按需渲染的区别

维度预加载 (preloadApp)按需渲染 (WujieVue)
时机登录成功后立即用户首次访问时
作用提前下载子应用静态资源创建子应用实例并挂载
资源仅网络请求网络 + DOM + JS 执行
目的减少首次打开延迟实际渲染子应用

5.4 子应用容器布局

// App.vue
.child-app-container {
  width: 100%;
  height: calc(100vh - 66px);  // 减去 Header 高度
  overflow: hidden;
}

// AuthenticatedLayout.vue
.authenticated-layout {
  display: flex;
  flex-direction: column;
  height: 100vh;
}
.custom-tabs-content {
  flex: 1;
  height: calc(100vh - 66px);
  overflow: hidden;
  position: relative;
}

5.5 子应用 Props 传递

主应用通过 WujieVue 的 :props 向子应用传递共享数据:

<WujieVue
  :props="{ token: userStore.token, userInfo: userStore.userInfo }"
/>

子应用通过 window.__WUJIE.props 读取:

// 子应用 wujie-bridge.ts
export function getWujieProps(): Record<string, any> {
  return (window as any).__WUJIE?.props || {}
}

六、子应用接入规范

6.1 子应用改造清单

每个子应用需要完成以下改造才能接入微前端:

步骤文件改动内容
1vite.config.ts配置 base: VITE_BASE_PATHserver.headers 添加 CORS
2.env.dev设置 VITE_DEV_PORTVITE_BASE_PATHVITE_APP_NAME
3src/utils/wujie-bridge.ts新建通信桥接器(环境检测、bus 通信、资源 404 检测)
4src/main.ts调用 errorCheck()
5src/App.vue移除旧的 iframe postMessage 监听

注意:路由同步由 wujie sync 模式自动处理,子应用无需编写任何路由同步代码。

6.2 子应用 wujie-bridge.ts 标准模板

// 核心导出
export { isInWujie, isInIframe }       // 环境检测
export { notifyMainApp }               // 向主应用发事件
export { onMainAppEvent, offMainAppEvent } // 监听主应用事件
export { errorCheck }                  // 资源 404 检测
export { MESSAGE_TYPE }                // 事件类型常量(TO_ROUTE, ASSETS_404, CLOSE_ALL_TABS)

6.3 主应用注册新子应用

  1. registry.ts 添加子应用配置(childPathList 自动从 registry 派生,无需手动维护)
  2. router/index.ts 添加占位路由 /{appName}/:pathMatch(.*)*
  3. .env.base 添加 VITE_{APP_NAME}_APP_URL
  4. tabs.ts appList 添加 Tab 配置

七、生产环境部署

7.1 Nginx 配置要点

# 主应用
location /micro-main/ {
  try_files $uri $uri/ /micro-main/index.html;
}

# 子应用(以 ibs-manage 为例)
location /ibs-manage/ {
  proxy_pass http://ibs-manage-server/;
  # 或静态文件
  # alias /path/to/ibs-manage/dist/;
  # try_files $uri $uri/ /ibs-manage/index.html;
}

7.2 URL 生成策略

开发环境:
  主应用: http://localhost:3000/micro-main/
  子应用: http://localhost:3007/ibs-manage/  (由环境变量 VITE_IBS_MANAGE_APP_URL 提供)
  WujieVue url = http://localhost:3007/ibs-manage/

生产环境:
  主应用: https://domain.com/micro-main/
  子应用: https://domain.com/ibs-manage/  (由 nginx 反向代理)
  WujieVue url = /ibs-manage/  (相对路径)

八、已知限制与后续规划

8.1 当前限制

限制说明影响
子应用使用 WebHistory子应用 router 使用 createWebHistory(BASE_URL),在 wujie 沙箱中 location 被代理子应用独立运行和微前端运行行为一致
菜单路径依赖后端 module 字段buildFullPath 根据 menu.module 拼接 /${appName} 前缀后端菜单配置需正确设置 module
预加载依赖子应用 dev server开发环境下子应用未启动时预加载会静默失败不影响功能,仅影响首次加载速度

8.2 后续规划

阶段内容优先级
Phase 3.1剩余 5 个子应用迁移到 monorepo
Phase 3.2子应用间直接通信(不经过主应用中转)
Phase 3.3子应用独立部署 + 版本管理

附录 A:完整数据流时序图

A.1 菜单点击 → 子应用渲染(sync 模式)

用户          SiderMenu      tabs.ts       Vue Router     App.vue        wujie(sync)    子应用
 │               │              │              │             │              │             │
 │──点击菜单──→  │              │              │             │              │             │
 │               │──menuClick──→│              │             │              │             │
 │               │              │──push────────→│             │              │             │
 │               │              │              │──路由变化───→│              │             │
 │               │              │              │             │──标记已访问   │             │
 │               │              │              │             │  (visitedApps)│             │
 │               │              │              │             │──v-show=true  │             │
 │               │              │              │             │              │             │
 │               │              │              │  URL 变化 → wujie sync 自动同步          │
 │               │              │              │             │──────────────→│──replace───→│
 │               │              │              │             │              │             │──渲染页面
 │               │              │              │             │              │             │
 ✅ 无需 bus 通信,无防循环标记,wujie 框架自动处理

A.2 子应用内部导航 → 主应用 URL 同步(sync 模式)

用户          子应用         wujie(sync)    Vue Router     App.vue
 │              │              │              │             │
 │──点击链接──→ │              │              │             │
 │              │──push────────│              │             │
 │              │              │              │             │
 │              │  路由变化 → wujie sync 自动同步到主应用 URL │
 │              │──────────────→│──replace────→│             │
 │              │              │              │──路由变化───→│
 │              │              │              │             │──仅标记已访问
 │              │              │              │             │
 ✅ 无需 bus 通信,地址栏自动更新为 /ibs-manage/operation/yyy

A.3 跨应用跳转(TO_ROUTE 事件)

用户          子应用A        wujie bus     AuthLayout     tabs.ts       Vue Router    wujie(sync)   子应用B
 │              │              │             │              │             │              │             │
 │──操作────→   │              │             │              │             │              │             │
 │              │──emit────────→│             │              │             │              │             │
 │              │  TO_ROUTE     │──收到───────→│              │             │              │             │
 │              │              │             │──menuClick──→│             │              │             │
 │              │              │             │              │──push───────→│              │             │
 │              │              │             │              │             │  URL 变化 → sync 自动同步   │
 │              │              │             │              │             │──────────────→│──replace───→│
 │              │              │             │              │             │              │             │──渲染