微前端落地大型项目:从怀疑人生到真香现场
上周五晚上十一点半,我正戴着耳机单曲循环《Blinding Lights》敲代码,钉钉突然弹出一条消息:“微前端方案下周必须上线,双11大促要用。”我盯着屏幕愣了三秒,心里默默问候了一下产品经理全家——不是说好用 monorepo 吗?怎么又改需求了?
但转念一想,也罢。在这家公司干了三年多,每天写着差不多的业务逻辑,连 bug 都开始重复出现。最近正在看新机会,简历上写“精通微前端架构”总比“熟练使用 if-else”听起来高级点。于是咬咬牙,撸起袖子,开始了这场“大型项目微前端化”的硬核改造。
为啥非得搞微前端?
事情还得从去年说起。我们主站是一个基于 React 的巨型应用,光是 node_modules 就有 2.3GB(别问,问就是历史包袱)。前端团队分成了五个小组,每个组负责不同模块,但共用同一个 Git 仓库、同一套构建流程。结果就是:
- 每次发布都要全员对齐,否则一个小组改了个按钮样式,另一个小组的表单就崩了;
- 老旧模块用的是 React 16,新模块想上 React 18,升级?门都没有,光是依赖冲突就能让你原地升天;
- 测试同学每次回归都像在玩扫雷,不知道哪块会被“意外优化”。
更离谱的是,有一次运维把 staging 环境的缓存配错了,导致用户看到的是三个月前的 UI —— 而我们花了整整两天才定位到问题不在代码里,而在 CDN 缓存策略上。
这时候,微前端的概念就像救命稻草一样飘了过来。翻了几本技术书籍,《Micro Frontends in Action》《前端架构:从入门到微前端》,越看越觉得这玩意儿简直是为我们量身定做的:独立开发、独立部署、技术栈无关——听着就爽!
选型:qiankun 还是 Module Federation?
一开始团队内部吵得不可开交。有人力推 Webpack 5 的 Module Federation,理由是“原生支持,不用引入第三方库”;另一派则坚持用 qiankun,说“社区成熟,文档齐全”。
我作为那个被迫加班的人,决定亲自下场测一把。花了一个周末,分别搭了两个 demo:
- Module Federation:配置确实灵活,但要求所有子应用必须用 Webpack 5 构建,而我们老项目还在用 Webpack 4,迁移成本太高;
- qiankun:虽然要加个 runtime,但对子应用侵入性极小,甚至可以用
<script>标签加载非 React 应用。
最终拍板用了 qiankun。理由很现实:老板不给时间重构老项目。
踩坑实录:那些让我想砸键盘的瞬间
坑 1:CSS 样式污染,谁动了我的按钮?
微前端最怕的就是样式冲突。主应用用的是 Ant Design 4.x,子应用 A 用了 5.x,子应用 B 干脆自己写了一套 CSS-in-JS。结果一集成,按钮字体忽大忽小,input 边框时有时无。
起初我想着靠 CSS Modules 或者 CSS Scope 解决,但 qiankun 默认并不隔离样式。后来翻官方文档才发现,可以配合 Shadow DOM 或 CSS Prefix。但我们项目 IE11 还没完全下线(别笑,真有),Shadow DOM 直接出局。
最后采用“暴力命名空间”方案:每个子应用的根容器加一个唯一 class,比如 subapp-order、subapp-user,然后所有样式都包在里面:
.subapp-order .ant-btn {
/* your styles */
}
虽然丑,但有效。测试同学终于不再提“按钮变透明”这种 issue 了。
坑 2:React Context 失效,状态传不动了!
主应用用 React Context 管理用户登录态和主题色。按理说子应用挂载后应该能继承这些 context,但实际发现子应用里的 useContext(AuthContext) 拿到的是 undefined。
查了半天源码,才发现 qiankun 在加载子应用时,会把子应用的 React 实例和主应用隔离开——两个 React 实例,自然共享不了 context。
解决方案有两个:
- 把共享状态提到全局 window 上(不推荐,脏);
- 通过 props 传递(qiankun 支持
props注入)。
我们选了第二种。在注册子应用时,把主应用的状态作为 props 传进去:
registerMicroApps([
{
name: 'order-app',
entry: '//localhost:8081',
container: '#subapp-container',
activeRule: '/order',
props: {
user: globalStore.user,
theme: globalStore.theme,
onThemeChange: (theme) => globalStore.setTheme(theme)
}
}
]);
子应用里通过 props 拿到状态,再用 useMemo 包一层模拟 context。虽然绕了点,但至少不炸。
坑 3:路由跳转混乱,页面刷没了!
我们的主应用用 React Router v5,子应用有的用 v5,有的用 v6。当用户从主应用跳转到子应用路由时,经常出现“白屏”或“404”。
问题出在 路由同步机制 上。qiankun 默认监听 popstate 和 hashchange,但如果你在子应用里用 history.push,主应用的路由并不会自动更新,导致刷新后找不到页面。
解决办法是统一使用 主应用的 history 实例。我们在主应用初始化时导出 history:
// main-app/history.js
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
然后通过 props 把这个 history 传给所有子应用。子应用内部不再创建自己的 history,而是直接用传进来的:
// subapp/router.js
const App = ({ history }) => (
<Router history={history}>
{/* routes */}
</Router>
);
这样一来,无论在哪跳转,URL 都由主应用统一管理,刷新也不怕了。
性能与体验:不能只顾着跑通
微前端搞定了功能,但性能差点翻车。首屏加载时间从 1.2s 暴涨到 3.8s!用户反馈“点一下卡半天”,产品经理差点把我钉在会议室墙上。
排查发现,问题出在 子应用懒加载 + 全量 JS 加载。qiankun 默认会在激活时加载整个子应用 bundle,哪怕你只用到一个页面。
我们做了三件事:
- 按路由拆包:子应用内部用 React.lazy + Suspense,确保只加载当前页面代码;
- 预加载:在用户 hover 导航菜单时,提前加载可能访问的子应用(参考淘宝做法);
- 资源缓存:利用 localStorage 缓存子应用的 entry HTML 和关键 JS,配合 ETag 校验。
优化后,首屏回到 1.5s,勉强能接受。
顺便提一句,千万别在子应用里用 document.title!因为多个子应用会互相覆盖。我们统一用主应用监听路由变化来设置 title。
开发体验:工具链救我狗命
微前端最大的痛点其实是本地开发。以前一个 npm start 就搞定,现在要同时启动主应用 + 三个子应用 + mock 服务,终端开了七八个 tab,内存直接飙到 16GB。
后来我写了个脚本,用 concurrently 一键启停:
{
"scripts": {
"dev": "concurrently \"npm run dev:main\" \"npm run dev:order\" \"npm run dev:user\""
}
}
还配了 VS Code 的 Remote Containers,把开发环境容器化,新人入职再也不用折腾 node 版本和 Python 环境了。
调试方面,Chrome DevTools 的 Network Throttling 和 Coverage 工具帮了大忙。特别是 Coverage,一眼看出哪些 JS 根本没执行,果断删掉。
效果如何?值不值得搞?
上线一个月后,数据来了:
| 指标 | 微前端前 | 微前端后 | 变化 |
|---|---|---|---|
| 发布频率 | 2次/周 | 15次/周 | ↑650% |
| 构建失败率 | 18% | 3% | ↓83% |
| 跨团队协作 issue | 42个/月 | 7个/月 | ↓83% |
| 首屏加载 | 1.2s | 1.5s | +0.3s |
虽然首屏慢了点,但团队效率提升是实打实的。现在每个小组可以自由选择 React 16/17/18,甚至 Vue(真的有个小组偷偷上了 Vue 3,没人发现)。
更重要的是,线上事故少了。以前一个小组的内存泄漏能让整个站点崩溃,现在最多影响自己的子应用。
写在最后:微前端不是银弹,但值得试试
说实话,微前端不是什么高深技术,它更像是一种工程妥协——当你无法一次性重构整个系统时,用架构手段把混乱控制在局部。
如果你的项目满足以下任一条件,建议认真考虑微前端:
- 多团队并行开发,互相阻塞严重;
- 技术栈陈旧,但无法整体升级;
- 需要快速集成第三方系统(比如外包团队写的模块)。
但如果你是个五人小团队,项目才写半年,别折腾了,monorepo + TypeScript + ESLint 足够你爽到明年。
至于我?简历已经更新:“主导微前端架构落地,支撑日活百万级应用”。下周一面试,希望别被问得太细……毕竟有些坑,只有深夜加班的人才懂。
对了,如果你也在搞微前端,强烈推荐那本《Micro Frontends in Action》,作者踩过的坑,我基本都复现了一遍——站在巨人的肩膀上摔跤,至少姿势好看点。

评论 0