一、背景:当项目越来越大,单体架构变成技术负担
广告平台经历多个阶段:
- 初期为单 SPA(Single Page Application),业务快速集中开发
- 中期模块增多、接口冲突、构建缓慢、协作困难
- 后期我推动将其演进为“可拆分、可独立部署、可跨项目复用”的模块架构
这也是走向微前端体系的自然过程。
二、判断是否该拆的三个信号
- 多团队并行开发 + 发布耦合严重
- 每次打包时间超过 30 秒
- 不同模块间基础依赖版本不一致,频繁冲突
三、拆分的三种方式对比
| 拆分方式 | 特点 | 适用场景 |
|---|---|---|
| 按功能模块划分 | 各模块目录独立,内部协作好 | 中大型 SPA 项目 |
| 按子项目部署 | 独立构建部署,主项目载入 | 高自治业务模块 |
| 微前端(MF)架构 | 独立技术栈、热更新、嵌套部署 | 多团队并行 & 复杂系统 |
四、广告平台拆分演进路线
- 初始结构:所有功能一个 Vue 项目,目录如下:
src/
views/
ad/
material/
audit/
- 拆出公共模块为包(组件库、工具库)
packages/
ui-lib/
utils/
- 独立打包业务模块(如 report)并通过主项目注册:
// main.ts
loadRemoteModule('report', 'https://cdn.xxx.com/report.js')
五、Module Federation 实战(vite + vue3)
使用 vite-plugin-federation 构建:
// vite.config.ts
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
plugins: [
federation({
name: 'report',
filename: 'remoteEntry.js',
exposes: {
'./ReportPage': './src/pages/Report.vue'
},
shared: ['vue', 'pinia']
})
]
})
主应用加载:
import('report/ReportPage').then(mod => {
app.use(mod.default)
})
六、拆分时常见问题与解法
1. 路由冲突
解决:子应用使用子路由前缀 + 独立 router 实例
2. 样式污染
解决:每个子应用打包为 scope 包或使用沙箱隔离(如 qiankun)
3. 状态同步困难
解决:使用 shared pinia store 或 postMessage 通信桥
七、构建与部署策略
- 每个子模块独立构建(CI),产物上传 CDN:
/cdn/{module}/{version}/ - 主项目使用远程引用 + 异步加载:
const loadModule = (name) => import(`https://cdn.xxx.com/${name}/latest/remoteEntry.js`)
- 保留主项目兜底页,防止子模块失败后空白
八、子项目本地调试与开发体验
通过 proxy + 环境变量模拟:
// .env.local
VITE_REPORT_URL=http://localhost:9001
主项目动态挂载:
const reportEntry = import.meta.env.VITE_REPORT_URL || 'https://cdn...'
import(`${reportEntry}/remoteEntry.js`)
九、总结
SPA 架构并不等于“不能拆”,而是要有“按需拆解 + 合理组合”的能力。
我在广告平台项目中,结合业务规模、团队组织和部署流程,逐步推进模块解耦 → 独立构建 → 微前端落地,实现了真正意义上的前端架构升级。