版本 : 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.vue 和 AuthenticatedLayout.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 } |
AuthenticatedLayout → menuClick |
ASSETS_404 |
子应用静态资源加载失败 | { appName } |
AuthenticatedLayout → 弹窗提示刷新 |
CLOSE_ALL_TABS |
子应用请求关闭所有 Tab | { appName } |
AuthenticatedLayout → removeTab |
主应用 → 子应用
| 事件名 | 触发时机 | 数据结构 | 处理方 |
|---|---|---|---|
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 预加载策略
在 AuthenticatedLayout 的 onMounted 中触发预加载,分优先级:
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_PATH,server.headers 添加 CORS |
| 2 | .env.dev |
设置 VITE_DEV_PORT、VITE_BASE_PATH、VITE_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 主应用注册新子应用
- registry.ts 添加子应用配置(
childPathList自动从 registry 派生,无需手动维护) - router/index.ts 添加占位路由
/{appName}/:pathMatch(.*)* - .env.base 添加
VITE_{APP_NAME}_APP_URL - 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───→│
│ │ │ │ │ │ │ │──渲染