基于 Vue 3 + TypeScript + Element Plus 和 Module Federation 技术构建的微前端后台管理系统,采用 RBAC 权限模型,支持 4 套环境配置和完整的子应用部署方案。
┌─────────────────────────────────────────────────────────┐
│ Shell 主应用 (:5003) │
│ ┌──────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ 路由守卫 │ │ 动态路由生成 │ │ Component Map 解析 │ │
│ └──────────┘ └─────────────┘ └──────────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐│
│ │ Layout (Sidebar + Navbar + TagsView) ││
│ └──────────────────────────────────────────────────────┘│
│ │ │ │
│ MF Remote Load MF Remote Load │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ system-admin (:5001)│ │ business (:5002) │ │
│ │ - UserList │ │ - Dashboard │ │
│ │ - RoleList │ │ - Orders │ │
│ │ - MenuList │ │ │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌──────────────────┐
│ Mock Server (:3000) │
│ Express + 内存 DB │
└──────────────────┘
| 分类 | 技术 |
|---|---|
| 框架 | Vue 3.5 + TypeScript 5.6 |
| UI 库 | Element Plus 2.14 |
| 状态管理 | Pinia 3.0 |
| 路由 | Vue Router 4.5 |
| 构建工具 | Vite 5.4 + @originjs/vite-plugin-federation 1.4 |
| 包管理 | pnpm 10 (workspace monorepo) |
| Mock 服务 | Express 4 + tsx |
| 部署 | Docker + Nginx |
mf-app/
├── pnpm-workspace.yaml # pnpm workspace 配置
├── package.json # 根 package.json(脚本 + pnpm overrides)
├── tsconfig.base.json # 共享 TS 配置
├── docker-compose.yml # Docker 编排
├── .github/
│ └── workflows/
│ └── ci-cd.yml # CI/CD 流水线
├── deploy/
│ ├── Dockerfile # 通用多阶段 Dockerfile
│ ├── Dockerfile.mock # Mock Server Dockerfile
│ └── nginx/ # Nginx 配置
│ ├── shell.conf
│ ├── system-admin.conf
│ └── business.conf
├── packages/
│ └── shared/ # @mf/shared 共享内核
│ └── src/
│ ├── types/ # RBAC 类型定义
│ ├── constants/ # 常量(白名单、存储键名等)
│ ├── utils/ # 工具(请求、权限、认证)
│ ├── stores/ # Pinia stores(user/permission/app/tagsView)
│ ├── directives/ # v-permission 指令
│ ├── api/ # API 接口
│ └── index.ts # 统一导出
├── mock-server/ # Express Mock Server
│ └── src/
│ ├── db.ts # 内存数据库(用户/角色/菜单/订单)
│ ├── middleware/auth.ts # JWT 认证中间件
│ └── routes/ # API 路由
├── apps/
│ ├── shell/ # 主应用
│ │ ├── src/
│ │ │ ├── main.ts # 应用入口
│ │ │ ├── App.vue
│ │ │ ├── router/ # 路由(守卫/静态路由/组件映射)
│ │ │ ├── layout/ # 布局(Sidebar/Navbar/TagsView)
│ │ │ └── views/ # 页面(登录/首页/错误页/重定向)
│ │ └── .env.* # 多环境配置
│ ├── system-admin/ # 子应用:系统管理
│ │ └── src/
│ │ ├── main.ts # 入口(独立模式/远程模块模式)
│ │ ├── router/ # 独立模式路由
│ │ └── views/ # UserList / RoleList / MenuList
│ └── business/ # 子应用:业务管理
│ └── src/
│ ├── main.ts
│ ├── router/
│ └── views/ # Dashboard / Orders
- Node.js >= 20
- pnpm >= 9
# 安装依赖
pnpm install
# 一键启动所有服务(Mock Server + 3 个前端应用)
pnpm dev当需要在本地开发时让主应用稳定消费子应用产物(remoteEntry.js)并支持子应用实时增量构建,使用:
pnpm dev:mf该命令会并发启动:
mock-server(:3000)shell开发服务(:5003)system-admin:vite build --watch+vite preview(:5001)business:vite build --watch+vite preview(:5002)
说明:
dev:mf模式下,子应用由preview提供dist/assets/remoteEntry.js,更接近线上行为。
启动后访问:
| 服务 | 地址 |
|---|---|
| Shell 主应用 | http://localhost:5003 |
| system-admin 子应用(独立模式) | http://localhost:5001 |
| business 子应用(独立模式) | http://localhost:5002 |
| Mock Server API | http://localhost:3000/api |
| 账号 | 密码 | 角色 | 权限范围 |
|---|---|---|---|
| admin | 123456 | 超级管理员 | 全部菜单 + 全部按钮权限 |
| editor | 123456 | 业务编辑员 | 首页 + 业务模块 |
| viewer | 123456 | 访客 | 仅首页 |
pnpm dev:mock # 仅启动 Mock Server
pnpm dev:shell # 仅启动 Shell 主应用
pnpm dev:system-admin # 仅启动 system-admin 子应用
pnpm dev:business # 仅启动 business 子应用每个应用支持 5 套环境配置文件:
| 文件 | 环境 | 说明 |
|---|---|---|
.env |
基础 | 所有环境共享的变量 |
.env.dev |
开发 | 本地开发,连接本地 Mock Server |
.env.test |
测试 | 测试环境,连接测试 API |
.env.uat |
预发布 | UAT 环境 |
.env.prod |
生产 | 生产环境,连接 CDN 和生产 API |
.env.docker |
Docker | Docker Compose 部署,通过 Nginx 代理 |
| 变量 | 说明 |
|---|---|
VITE_API_BASE_URL |
后端 API 基础路径 |
VITE_PORT |
开发服务器端口 |
VITE_SYSTEM_ADMIN_REMOTE_URL |
system-admin 子应用 remoteEntry.js 地址 |
VITE_BUSINESS_REMOTE_URL |
business 子应用 remoteEntry.js 地址 |
VITE_STANDALONE |
子应用是否独立运行模式 |
当前 dev 环境默认端口:
shell:5003system-admin:5001business:5002
用户 → 角色 → 菜单(目录/菜单/按钮)
- 目录:侧边栏分组,如「系统管理」
- 菜单:实际页面路由,如「用户管理」
- 按钮:页面内操作权限点,如「新增用户」「删除用户」
登录 → 获取 token
→ 路由守卫检测 token
→ 调用 getUserInfo 获取角色/权限/菜单树
→ generateRoutes 将菜单树转为动态路由
→ addRoute 注册到 Vue Router
→ 重新导航到目标页面
使用 v-permission 指令控制按钮显示:
<el-button v-permission="'system:user:add'">新增</el-button>
<el-button v-permission="['system:user:edit', 'system:user:delete']">操作</el-button>以下依赖通过 MF singleton: true 共享,确保跨应用单例:
vue— 确保只有一个 Vue 实例vue-router— 确保只有一个路由实例pinia— 确保状态共享(defineStore 按 ID 缓存)element-plus— 避免重复加载 UI 库@element-plus/icons-vue— 图标单例@mf/shared— 跨应用共享请求客户端、权限指令与公共 store 能力
菜单数据中的 component 字段是字符串,通过组件映射器解析为懒加载组件:
| component 值 | 解析结果 |
|---|---|
Layout |
路由分组占位组件(仅承载子路由,不重复渲染 Shell 顶层布局) |
shell/Dashboard |
Shell 本地视图 views/dashboard/index.vue |
system-admin/UserList |
通过 MF 远程加载 system-admin 的 UserList |
business/Orders |
通过 MF 远程加载 business 的 Orders |
子应用通过 VITE_STANDALONE 环境变量控制运行模式:
true:独立开发模式,自带路由和布局,可独立运行调试false:远程模块模式,仅暴露组件供宿主加载
# 构建所有应用(生产模式)
pnpm build:prod
# 按环境构建
pnpm build:dev # 开发环境
pnpm build:test # 测试环境
pnpm build:uat # 预发布环境
pnpm build:prod # 生产环境# 一键启动全栈(Mock Server + 3 个前端应用)
docker-compose up -d --build
# 访问 http://localhost:8080Module Federation 要求子应用先于主应用部署:
# 1. 先部署子应用
docker-compose up -d mf-business
docker-compose up -d mf-system-admin
# 2. 再部署主应用
docker-compose up -d mf-shell子应用的 Nginx 配置包含 MF 必需的 CORS 和缓存策略:
remoteEntry.js:设置Access-Control-Allow-Origin *,不缓存__federation_*.js:设置 CORS 头,长缓存(immutable)index.html:不缓存
GitHub Actions 流水线(.github/workflows/ci-cd.yml):
- 质量检查:TypeScript 类型检查
- 构建子应用:remote-first,先构建 system-admin 和 business
- 构建主应用:构建 shell
- Docker 镜像:构建并推送到 GHCR
- 部署:输出部署指令
- 在
apps/下创建新应用,配置vite.config.ts的 federationexposes - 在 Shell 的
vite.config.ts中添加remotes配置 - 在 Shell 的
src/remote-modules.d.ts中添加模块类型声明 - 在 Mock Server 的菜单数据中添加
component: 'new-app/ComponentName' - 在 Shell 的
component-map.ts中确认解析逻辑(已自动处理远程导入)
- 在 Mock Server 的
db.ts中添加菜单/按钮数据 - 将权限码分配给对应角色的
menuIds - 在页面中使用
v-permission="'module:resource:action'"控制按钮
# 检查所有应用
npx vue-tsc --noEmit -p apps/shell/tsconfig.json
npx vue-tsc --noEmit -p apps/system-admin/tsconfig.json
npx vue-tsc --noEmit -p apps/business/tsconfig.json优先确认以下两点:
- 对应子应用服务是否启动并监听正确端口(
5001/5002) remoteEntry.js返回内容是否为 JS(不是index.html)
可直接验证:
curl -i http://localhost:5001/assets/remoteEntry.js
curl -i http://localhost:5002/assets/remoteEntry.js- 推荐先使用
pnpm dev:mf联调模式重启全部服务 - 确认三端 federation 都包含
@mf/shared共享配置 - 清理 shell 的 Vite 缓存后重启:
rm -rf apps/shell/node_modules/.vite
pnpm dev:mf动态路由中的 Layout 仅用于目录分组,不应再次映射为 Shell 顶层布局。
项目已将其映射为路由占位组件(仅 router-view),避免双层布局重复渲染。
| 对比维度 | 当前方案:Module Federation(Vite 插件) | 乾坤(qiankun) |
|---|---|---|
| 架构形态 | 运行时“模块级”拼装(remote module) | 运行时“应用级”加载(micro app) |
| 性能与体积 | 更优:依赖可共享、按需加载更细粒度 | 通常偏重:子应用更像完整应用被加载 |
| 依赖复用 | 强:shared 可做单例(如 vue/pinia) |
可复用但治理成本更高,重复依赖更常见 |
| 技术栈兼容 | 更适合同技术栈、同构建体系 | 更强:异构技术栈接入更友好 |
| 隔离能力 | 原生隔离较弱,靠规范治理冲突 | 更强:内置沙箱、样式隔离机制 |
| 接入复杂度 | 中等:需处理 exposes/remotes/static import 约束 | 中等偏低:注册子应用 + 生命周期即可启动 |
| 调试体验 | 同栈项目体验好,接近本地模块开发 | 跨应用生命周期与通信调试成本更高 |
| 路由整合 | 宿主主导,组件/页面映射灵活 | 以子应用挂载/卸载为中心,路由边界更明显 |
| 独立部署 | 支持,remoteEntry 驱动 |
支持,子应用独立发布 |
| 适配当前项目 | 高匹配(Vue3 + Vite + shared 包统一) | 可用,但优势主要在异构和强隔离场景 |
结论:当前项目采用 Module Federation 是更合适的,优势在性能、共享依赖与统一技术栈协同;若未来演进为多技术栈并存且隔离诉求更强,可再评估乾坤。