微前端?县城做题家在上海搞大项目的一地鸡毛实录
大家好,我是老张(不是那个卖烤红薯的老张),一个从三线小县城一路“卷”到上海的小镇做题家。目前在一家中型互联网公司远程办公——虽然说是远程,但其实就租了个离公司步行十分钟的房子,毕竟“远程”两个字听起来高级点,实际不过是老板省了办公室空调费罢了。
平时不用 VS Code,Vim 是我的命根子(别问为什么,问就是 :wq 有信仰)。最近半年被拉进一个“史诗级”重构项目:把一个单体前端应用拆成微前端架构。说白了,就是领导看了几篇 GitHub 热榜文章,拍脑袋说:“我们要解耦!要独立部署!要‘乐高式开发’!” —— 于是,我这个刚搞定 CSS 动画帧率优化的“交互小能手”,就被扔进了微前端的深水区。
今天不吹技术牛逼,就说说这一路踩过的坑、熬过的夜、和产品经理互相伤害的故事。如果你也在县城老家刷 GitHub 想跳槽来大城市搞大项目,这篇文章可能就是你的前车之鉴。
起因:不是我想搞微前端,是 deadline 追着我跑
事情得从去年双11说起。我们公司那个主站,是个典型的“祖传代码”:React + Ant Design + 五年没人敢动的业务逻辑,打包出来快 8MB,首屏加载慢得像用 2G 刷抖音。更惨的是,五个业务团队共用一个 repo,改个按钮颜色都能引发 merge conflict 大战。
某天晨会,CTO 拿着手机刷 GitHub Trending,突然拍桌:“你们看看人家 qiankun 星标都 20k+ 了!我们还在搞 monolith?太 low 了!”
于是,微前端立项。目标很明确:各业务模块独立开发、独立部署、互不影响。理想很丰满,现实嘛……后面你就知道了。
作为团队里“对前端动画和交互比较敏感”的人(其实就是被排期时没跑掉),我成了微前端落地的主要 implementer。说实话,当时内心 OS 是:“我连 Webpack 配置都还没吃透,你让我搞微前端?”
第一关:资源隔离?别闹了,JS 全局变量比亲戚还亲
微前端最怕什么?样式污染 和 全局状态冲突。我们主应用用的是 React 17,而一个老保险模块还在用 Vue 2 + jQuery(别笑,真有)。第一次集成测试那天,我信心满满地跑起本地 dev server,结果页面一加载——按钮全变蓝了,下拉框疯狂闪烁,控制台报错:
Uncaught TypeError: Cannot read property 'map' of undefined
查了半天,发现是子应用偷偷往 window 上挂了个 config 对象,结果主应用也用了同名变量,直接覆盖了。那一刻我真的想砸电脑。
后来我们定了三条“军规”:
- 所有子应用必须沙箱化:qiankun 的
sandbox: { strictStyleIsolation: true }必开,虽然性能有点损耗,但至少 CSS 不会串门。 - 禁止往 window 挂东西:谁再这么干,团建请全组喝奶茶。
- 公共资源统一管理:比如 lodash、moment.js 这些,由主应用提供,子应用通过
shared机制消费,避免重复加载。
说到资源,这里有个血泪教训:子应用的静态资源路径必须绝对路径!否则在主应用路由下加载子应用时,图片、字体全 404。我们后来统一约定所有子应用部署到 /subapp-xxx/ 路径下,并在构建时注入 publicPath:
// webpack.config.js
output: {
publicPath: process.env.NODE_ENV === 'production'
? 'https://cdn.yourcompany.com/subapp-insurance/'
: '/'
}
不然线上用户看到的页面,就是一堆占位图配文字:“加载失败,请联系客服”——而客服根本不知道前端改了架构。
第二关:GitHub 不是万能的,但没它真不行
微前端涉及多个 repo 协同,这时候 GitHub 就成了我们的“救命稻草”。每个子应用单独一个 repo,主应用一个 repo,再加一个 shared-components 库。但问题来了:如何同步依赖版本?
一开始我们各自升级 React,结果主应用 React 17,子应用 React 18,一集成就报 hooks 错误。后来学乖了,在 GitHub 上建了个 frontend-monorepo-spec 文档,明确规定:
| 依赖 | 版本 | 负责人 |
|---|---|---|
| React | 17.0.2 | 主应用团队 |
| Ant Design | 4.24.0 | UI 组件组 |
| qiankun | 2.10.10 | 架构组 |
每次升级都要 PR + 团队评审。虽然流程繁琐,但至少避免了“我在本地跑得好好的,上线就崩”的经典场景。
另外,GitHub Actions 也帮了大忙。我们给每个子应用配了 CI,只要 push 到 main 分支,自动构建并推送到 CDN。主应用通过配置文件动态加载最新子应用地址:
// apps.json
[
{
"name": "insurance",
"entry": "https://cdn.yourcompany.com/subapp-insurance/latest/index.html"
}
]
这样,子应用发版再也不用等主应用重新部署——产品经理终于不用半夜打电话骂我了(感动)。
第三关:用户体验?别忘了前端是给人用的!
很多人搞微前端只关心架构,却忘了用户根本不在乎你是不是微前端。他们只关心:页面快不快?动效流不流畅?会不会白屏?
我们第一次上线,子应用切换时整个页面 reload,产品经理当场暴怒:“这还不如以前呢!” 后来我们做了三件事:
- 预加载子应用:在主应用空闲时(比如用户看 banner 广告时),悄悄 fetch 子应用的 entry HTML 和 JS。
- 骨架屏兜底:子应用加载期间,显示对应模块的骨架屏,避免白屏。
- 动画过渡:用 CSS
transform做淡入淡出,而不是生硬的display: none/block。毕竟我可是靠动画吃饭的人(笑)。
性能方面,我们用 Lighthouse 测了一波:
| 指标 | 单体应用 | 微前端(优化后) |
|---|---|---|
| FCP | 2.8s | 2.1s |
| TTI | 5.3s | 3.9s |
| Bundle Size | 7.8MB | 主应用 1.2MB + 按需加载 |
关键点在于:主应用轻量化,子应用按路由懒加载。用户进首页,只加载首页相关子应用,其他模块等点击再加载。
不过浏览器兼容性还是个坑。qiankun 依赖 Proxy,IE11 直接躺平。最后我们不得不为 IE 用户降级:走传统 iframe 方案(别打我,业务需求…)。还好现在 IE 流量不到 0.3%,勉强能接受。
调试?Vim + Chrome DevTools 才是真·生产力
作为 Vim 党,我坚决不用 IDE 的图形化调试器。微前端调试最烦的是:子应用的 sourcemap 在主应用里找不到。
解决方案?简单粗暴:
- 本地开发时,所有子应用启动在不同端口(3001, 3002...)
- 主应用配置 proxy,把
/subapp-insurance代理到http://localhost:3001 - Chrome DevTools 里 Sources 面板手动添加 workspace folder,映射到本地子应用源码
这样就能直接打断点、看变量,和单体应用体验差不多。唯一的问题是:每次新加子应用,都得改主应用的 proxy 配置——运维同事说我改配置的频率比他改 nginx 还勤。
总结:微前端不是银弹,但也不是毒药
折腾半年,项目终于稳定上线。现在各团队可以并行开发,保险模块上周五晚上加班发版,完全不影响电商模块第二天搞 618 活动。虽然偶尔还有样式冲突的小 bug,但比起以前“牵一发动全身”的恐惧,已经幸福太多。
几点真心话:
- 别为了微前端而微前端:如果团队就三个人,业务也不复杂,老老实实用 monorepo 吧。
- 自动化是生命线:CI/CD、依赖管理、错误监控,缺一不可。
- 用户体验永远第一:架构再漂亮,用户觉得卡,等于零。
最后,感谢 GitHub 上那些开源作者,没有 qiankun、single-spa 这些项目,我们可能还在手动拼 HTML。也感谢我那台服役五年的 MacBook,上周五凌晨三点还在跑 webpack,风扇声比蚊子叫还响——但它没炸,真争气。
如果你也在县城刷 GitHub,梦想着来上海搞大项目,我想说:技术没有高低贵贱,县城做题家也能写出优雅的代码。只是记得,别信领导嘴里的“简单重构”,那往往意味着一个月的加班和无数杯瑞幸。
好了,我去改产品经理刚提的“微前端里加个粒子动效”需求了。他说“就改一行代码”。呵呵。
(完)

评论 0