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

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


一、架构总览

1.1 系统拓扑

java 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    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 中统一注册:

子应用名称 端口 activeRule entry 状态
mkt 3001 /mkt /mkt/ 待迁移
commerce-finance 3002 /commerce-finance /commerce-finance/ 待迁移
doc 3003 /doc /doc/ ✅ 已迁移
operation 3004 /operation /operation/ 待迁移
general 3005 /general /general/ 待迁移
common 3006 /common /common/ 待迁移
ibs-manage 3007 /ibs-manage /ibs-manage/ ✅ 已迁移

1.3 Monorepo 项目结构

ruby 复制代码
微前端/
├── 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 对用户无感,子应用路径直接拼接在主应用路径后。

javascript 复制代码
主应用 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) 模式,路由分为三层:

bash 复制代码
路由树:
├── /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 协同控制主应用内容与子应用容器的显隐:

makefile 复制代码
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-content child-app-container 说明
/ (首页) ✅ 显示 ❌ 隐藏 findAppByRoute 返回 undefined
/profile ✅ 显示 ❌ 隐藏 主应用页面
/ibs-manage/xxx ❌ 隐藏 ✅ 显示 子应用路由,前缀匹配
/doc/xxx ❌ 隐藏 ✅ 显示 子应用路由

2.4 子应用 URL 生成

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

typescript 复制代码
// 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):

env 复制代码
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 通信、无需防循环标记

vue 复制代码
<!-- 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

bash 复制代码
┌─────────────────────────────────────────────────────────────┐
│                     路由同步数据流                             │
│                                                             │
│  场景 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 中对路径进行规范化,兼容不同来源的路径格式:

typescript 复制代码
// 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 保活模式

vue 复制代码
<!-- 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 按需渲染策略

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

typescript 复制代码
// 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 })

生命周期

ini 复制代码
用户首次访问 /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 中触发预加载,分优先级:

typescript 复制代码
// 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 子应用容器布局

scss 复制代码
// 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 向子应用传递共享数据:

vue 复制代码
<WujieVue
  :props="{ token: userStore.token, userInfo: userStore.userInfo }"
/>

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

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

六、子应用接入规范

6.1 子应用改造清单

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

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

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

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

typescript 复制代码
// 核心导出
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 配置要点

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 生成策略

bash 复制代码
开发环境:
  主应用: 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 模式)

dart 复制代码
用户          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 模式)

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

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

bash 复制代码
用户          子应用A        wujie bus     AuthLayout     tabs.ts       Vue Router    wujie(sync)   子应用B
 │              │              │             │              │             │              │             │
 │──操作────→   │              │             │              │             │              │             │
 │              │──emit────────→│             │              │             │              │             │
 │              │  TO_ROUTE     │──收到───────→│              │             │              │             │
 │              │              │             │──menuClick──→│             │              │             │
 │              │              │             │              │──push───────→│              │             │
 │              │              │             │              │             │  URL 变化 → sync 自动同步   │
 │              │              │             │              │             │──────────────→│──replace───→│
 │              │              │             │              │             │              │             │──渲染
相关推荐
Max_uuc3 小时前
【架构心法】对抗熵增:嵌入式系统中的“数据完整性”保卫战
架构
前端大卫3 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘4 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare4 小时前
浅浅看一下设计模式
前端
Lee川4 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix4 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人4 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl4 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Grok API 申请及使用
前端