启动所有子应用 + 主应用
微前端落地实战:我在深圳搞大型项目拆分的血泪史
大家好,我是阿哲,普通一本CS专业的大四狗,已经拿下了深圳某鹅厂系公司的offer,现在处于等入职前的“养老期”。不过别误会,我可不是躺平——去年秋招后就被拉进一个内部孵化项目打杂,没想到阴差阳错成了微前端架构的“小白鼠”。
事情是这样的:我们团队负责一个面向企业客户的SaaS平台,前端代码库已经有三年历史,主应用臃肿到连 git status 都要卡两秒。产品经理动不动就说“这个模块下周上线”,结果开发改个按钮颜色都要全量构建、全量部署,测试同学天天在群里@我:“你这改的是登录页,怎么把报表页干挂了?”
痛定思痛,技术负责人拍板:上微前端!但没人真正做过,文档看一堆,GitHub 上 star 最高的 qiankun 也只敢在 demo 里跑。我这个“即将入职的应届生”被临时抓壮丁,美其名曰“培养新人”,实则是没人愿意踩坑(笑)。
为什么是微前端?不是组件库?
一开始我也疑惑:为啥不直接搞个共享组件库 + monorepo?后来发现根本行不通。
我们的系统有四个核心业务线:客户管理、订单中心、数据分析、权限控制,每个团队独立迭代,甚至用的技术栈都不同——主应用是 Vue2,数据分析那边偷偷上了 React 18,还有个老模块还在用 jQuery(别问,问就是“祖传代码不能动”)。
这时候微前端的优势就出来了:解耦部署、独立技术栈、按需加载。我们不需要统一框架,只要约定好通信机制和样式隔离,就能让各个子应用像“插件”一样热插拔。
踩坑一:乾坤(qiankun)真的能“千坤”吗?
我们选型时对比了 single-spa、micro-app 和 qiankun。考虑到文档完整性和国内社区活跃度(腾讯系内部也有不少项目在用),最终选了 qiankun。
但上手第一天就翻车了。子应用打包后资源路径全错,主应用加载时报 404:
GET http://localhost:8080/js/chunk-vendors.js 404 (Not Found)
查了半天才发现,Vue CLI 默认的 publicPath 是 /,而微前端要求子应用必须知道自己的“挂载基路径”。解决方法是在 vue.config.js 里动态设置:
// vue.config.js
const { name } = require('./package.json');
module.exports = {
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
},
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
// 关键!动态 publicPath
chainWebpack: config => {
config.output.set('publicPath', 'auto');
}
};
另外,本地开发时主子应用端口不同,跨域问题也得处理。我们直接在 devServer 加了 CORS 头,简单粗暴但有效。
踩坑二:CSS 样式污染,谁动了我的按钮?
最恐怖的一次线上事故:数据分析团队更新了一个全局 .btn 样式,结果把主应用的提交按钮变成了粉色圆角——客户投诉说“你们系统变少女风了?”
微前端的样式隔离必须做!qiankun 提供了两种方案:沙箱隔离(experimentalStyleIsolation) 和 Shadow DOM。我们试了前者,但性能损耗明显(尤其在低端安卓机上),最终采用 CSS Module + BEM 命名规范 强制约束,并配合构建时注入唯一前缀:
/* 子应用 order-center 的按钮 */
.order-center__submit-btn {
background: #007aff;
}
同时,在主应用加载子应用时,我们用 fetch 预加载 CSS 并动态插入 <style> 标签,避免 FOUC(Flash of Unstyled Content)。用户体验这块,不能将就。
踩坑三:父子应用通信,别再用 localStorage 了!
早期有人提议用 localStorage + storage 事件传数据,我当场表演一个瞳孔地震——这玩意儿同步不可靠、调试困难、还容易被清理。
我们最终采用 qiankun 的 props 透传 + 全局状态管理(基于 RxJS 的轻量 Store)。主应用初始化时注入公共信息:
// 主应用
loadMicroApp({
name: 'order-center',
entry: '//localhost:8081',
container: '#subapp-container',
props: {
user: currentUser,
theme: currentTheme,
eventBus: globalEventBus, // 自定义事件总线
}
});
子应用通过 props 拿到 eventBus,即可订阅/发布消息:
// 子应用
export async function mount(props) {
props.eventBus.on('themeChange', handleTheme);
}
这种方式既解耦又可控,比瞎搞 window.xxx 安全多了。
性能优化:别让微前端变成“微卡顿”
微前端最大的性能陷阱是重复加载公共依赖。比如主应用和子应用都用了 Lodash,结果用户要下载两份。
我们的解决方案:
- 主应用提供共享依赖:通过 Webpack 的
externals把 Vue、Lodash 等暴露到window - 子应用按需引入:子应用配置
externals,直接引用全局变量
// 主应用 webpack
externals: {
vue: 'Vue',
lodash: '_'
}
// 子应用 vue.config.js
configureWebpack: {
externals: {
vue: 'Vue',
lodash: '_'
}
}
实测首屏加载时间从 4.2s 降到 2.1s,Lighthouse 分数提升 30+。
为了更直观,整理了个对比表:
| 指标 | 单体应用 | 微前端(初期) | 微前端(优化后) |
|---|---|---|---|
| 首屏加载 | 2.8s | 4.5s | 2.1s |
| JS 体积 | 3.2MB | 4.7MB | 2.9MB |
| 独立部署 | ❌ | ✅ | ✅ |
| 技术栈自由 | ❌ | ✅ | ✅ |
工具链和文档:别让新人两眼一抹黑
项目中期,新来的实习生看了三天代码还是不会本地联调。于是我们做了两件事:
- 写了一份 《微前端开发手册》(其实就是 Notion 页面),详细说明如何启动主/子应用、如何调试、常见报错
- 在 GitHub 仓库根目录放了
DEVELOPMENT.md,一键脚本搞定环境
npm run dev:all
另外,强烈推荐搭配 Vite + qiankun 插件,HMR 快得飞起。虽然我们项目因为历史原因还在用 Webpack,但新子应用已经全面 Vite 化。
最后一点真心话
微前端不是银弹。如果你的项目只有两个页面、一个团队维护,千万别为了“高大上”硬上。它解决的是组织架构复杂性带来的工程问题,而不是技术炫技。
我自己也是边学边干,期间翻烂了《微前端实战》这本书(机械工业出版社那本),还扒了 GitHub 上十几个开源项目源码。有时候凌晨两点盯着控制台报错,真想删库跑路。但当看到四个子应用独立部署、互不影响地跑在线上,那种成就感——值了。
现在我已经把这段经历写进了简历,面试官问起时还能吹两句“架构设计”。等入职后,说不定还能给新团队安利这套方案(前提是别遇到祖传 jQuery 项目 😅)。
共勉。

评论 0