dev.sh
微前端不是银弹,但救了我们的双11
去年双11前两周,我差点把键盘扔出窗外。
事情是这样的:我们公司是个典型的三线城市互联网团队,做本地生活服务平台,产品线杂、人手紧、需求多得像韭菜——割完一茬又长一茬。当时主站用的是 React + Ant Design,前端代码已经膨胀到 80 多个页面,打包后 vendor chunk 超过 3.5MB(gzip 后也有 1.2MB),首屏加载在低端安卓机上经常卡成 PPT。产品经理小李还天天催:“老板说要加一个独立的商家运营后台,下个月上线!”
我说:“咱这单体应用都快散架了,再塞新模块怕是要崩。”
他眨眨眼:“你不是技术负责人吗?搞不定就别当‘负责人’了。”
行吧,被逼上梁山。正好那会儿我在家远程办公,边听 Lo-fi Beats 边刷 GitHub,偶然看到几个开源微前端方案——qiankun、Module Federation、Piral……脑子一热:要不试试微前端?
为什么选微前端?不是为了“时髦”
说实话,我对“架构潮流”一直挺警惕的。之前有同事提议上 WebAssembly,我说:“咱们连 SSR 都没跑通,先别整花活。”但这次不一样。
痛点太真实了:
- 团队并行开发难:两个小组改同一个 Git 仓库,PR 冲突频发,Code Review 动不动就拖三天。
- 部署耦合严重:改了个按钮颜色,全站重新构建部署,测试回归成本高。
- 技术栈僵化:老项目用 React 16,想用新特性(比如 Concurrent Mode)?门都没有。
- 性能瓶颈:用户进首页,却要把整个商家管理、订单系统、营销活动的 JS 全下载下来。
微前端的核心价值,不是“拆”,而是“解耦”。每个子应用独立开发、独立部署、独立运行,主应用只负责路由分发和沙箱隔离。听起来像微服务?对,但它跑在浏览器里。
技术选型:GitHub 上淘金
我花了三天时间,在 GitHub 上对比主流方案:
| 方案 | 加载方式 | 沙箱能力 | 子应用通信 | 支持框架 | 学习曲线 |
|---|---|---|---|---|---|
| qiankun (蚂蚁) | JS Entry / HTML Entry | 强(JS 沙箱 + CSS 隔离) | 全局状态 + 自定义事件 | 任意(React/Vue/Angular) | 中等 |
| Webpack Module Federation | 构建时联邦 | 弱(依赖构建配置) | import() + shared module | Webpack 5+ | 高 |
| single-spa | 路由驱动 | 无(需自行处理) | customEvent / props | 任意 | 低 |
我们最终选了 qiankun。原因很现实:
- 我们团队 JavaScript 水平参差不齐,Module Federation 需要深入理解 Webpack 5,风险太高;
- single-spa 没有内置沙箱,CSS 样式污染问题得自己兜底,我可不想半夜被叫起来修“按钮变蓝了”的 bug;
- qiankun 文档全、社区活跃,GitHub 上 Star 超 20k,出了问题能快速搜到解决方案。
而且,它支持 HTML Entry ——子应用只要暴露一个 index.html,主应用就能动态加载,连构建产物都不用关心。对我们这种“历史包袱重”的项目太友好了。
实战:从单体到微前端的“缝合术”
第一步:主应用改造
主应用保留核心布局(Header + Sidebar + Main),用 registerMicroApps 注册子应用:
// main.js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'merchant-app', // 子应用名称
entry: '//localhost:3001', // 开发环境地址
container: '#subapp-container', // 挂载点
activeRule: '/merchant', // 路由匹配规则
},
{
name: 'marketing-app',
entry: '//localhost:3002',
container: '#subapp-container',
activeRule: '/marketing',
}
]);
start({
sandbox: { strictStyleIsolation: true }, // 开启严格样式隔离
});
注意:线上环境
entry要换成 CDN 地址,比如https://cdn.ourcompany.com/merchant/latest/index.html
第二步:子应用适配
子应用几乎不用大改!只需导出三个生命周期函数:
// merchant-app/src/main.js
let app = null;
export async function bootstrap() {
console.log('merchant app bootstrap');
}
export async function mount(props) {
app = createApp(App);
app.mount('#merchant-root');
}
export async function unmount() {
app.unmount();
}
重点来了:子应用必须能独立运行!我们在 package.json 里加了个判断:
{
"scripts": {
"dev": "cross-env IS_MICRO=false react-scripts start",
"dev:micro": "cross-env IS_MICRO=true react-scripts start"
}
}
然后在入口文件里:
// index.js
if (!window.__POWERED_BY_QIANKUN__) {
// 独立运行
ReactDOM.render(<App />, document.getElementById('root'));
} else {
// 作为子应用,只导出生命周期
// ...
}
这样开发时能单独启动调试,不用每次都跑主应用。
踩坑实录:那些让我想砸电脑的瞬间
坑1:CSS 样式污染
虽然开了 strictStyleIsolation,但子应用用了全局 CSS Reset,还是把主站字体干没了。后来强制要求所有子应用使用 CSS Modules 或 Scoped CSS,并在 CI 流程里加了 ESLint 规则:禁止 .css 文件中出现非局部类名。
坑2:公共依赖重复加载
主应用和子应用都用了 React 17,结果浏览器加载了两份 React,内存直接翻倍。解决方案:在 start() 里配置 singular: true(确保同一时间只有一个子应用激活),并通过 external 共享:
// 主应用 webpack.config.js
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM'
}
};
子应用也做同样处理,并通过 <script> 在主 HTML 中提前引入 React。
坑3:本地联调地狱
三个子应用 + 主应用,五个终端同时跑,端口冲突、跨域报错、缓存失效……后来写了个 shell 脚本一键启动:
concurrently \
"cd main && npm run dev" \
"cd merchant-app && npm run dev:micro" \
"cd marketing-app && npm run dev:micro"
配合 concurrently 和 wait-on,终于不用手动开 N 个 terminal 了。
效果:性能提升 + 团队效率起飞
上线三个月后,数据说话:
| 指标 | 改造前 | 改造后 | 提升 |
|---|---|---|---|
| 首屏加载 (3G) | 4.2s | 1.8s | ↓57% |
| JS Bundle Size | 3.5MB | 0.9MB (主应用) | ↓74% |
| 部署频率 | 1次/周 | 3-5次/天(子应用独立发布) | ↑500% |
| PR 冲突率 | 高频 | 几乎为零 | ✅ |
更重要的是,新来的实习生也能快速上手商家后台模块,不用再啃 20 万行的 monorepo。产品经理小李现在见我就笑:“你们技术真牛,上次改需求,当天就上线了!”
关于安全,我多啰嗦几句
微前端放大了攻击面——子应用可能来自不同团队,甚至第三方供应商。我们做了三件事:
- CSP 限制:主应用设置严格的
Content-Security-Policy,禁止 inline script; - Entry 白名单:子应用 entry URL 必须走公司内网或可信 CDN,禁止任意域名;
- 沙箱加固:开启
sandbox: { experimentalStyleIsolation: true },防止 CSS 劫持。
别觉得 paranoid,去年某大厂就因为微前端没隔离好,导致 XSS 跨子应用传播。安全不是功能,是底线。
写在最后:微前端适合你吗?
如果你的项目满足以下任一条件,可以考虑微前端:
- 团队超过 10 人,多个小组并行开发;
- 应用模块边界清晰(如 C 端 / B 端 / 运营后台);
- 有遗留系统需要渐进式重构;
- 对首屏性能有强要求。
但如果你只是做一个内部管理后台,就别折腾了——微前端解决的是复杂度问题,不是代码质量问题。我见过有人为了用 qiankun,硬把一个 5 个页面的应用拆成 3 个子应用,结果维护成本翻倍,纯属自虐。
我现在每天在家撸代码,听着音乐,看着子应用独立部署的日志流水,感觉世界清净多了。上周五晚上十点,收到一条告警:商家后台 404。我淡定地查了下,发现是子应用 CDN 配置错了——只影响该模块,主站稳如老狗。修完 bug,继续听我的《Coding Jazz》,舒服。
技术没有银弹,但合适的架构,真的能让你少掉几根头发。共勉。
本文代码示例已脱敏并上传至 GitHub 私有仓库(公司政策),如有兴趣可私信交流。
—— 一个在三线城市边听歌边写 React 的技术负责人

评论 0