"我们的前端单体太大了,几百个开发者撞车""不同业务想用不同技术栈" —— 微前端是解决这类大型组织问题的架构方案。但它不是银弹,引入不当反而增加复杂度。这篇文章把微前端的几种实现方式、适用场景、生产实践讲透。
微前端解决什么
大型前端项目的典型痛点:
- 代码库太大,构建几分钟到几十分钟。
- 几十个团队改同一个仓库,git 冲突频繁。
- 升级技术栈牵一发动全身。
- 不同业务的发布节奏不一样,无法独立部署。
微前端把前端按业务拆成多个独立子应用,各自开发、构建、部署,运行时在主应用里组合显示。类比微服务,但在浏览器端。
四种主流实现方式
1. iframe
最简单粗暴,各子应用独立 URL,主应用用 iframe 嵌入。
<iframe src="https://billing.app.com" frameborder="0"></iframe>
<iframe src="https://users.app.com" frameborder="0"></iframe>
优点:天然隔离(CSS / JS / window 完全独立)、技术栈无关、安全性高。
缺点:UX 差(URL 同步、弹窗、cookie 共享都麻烦)、性能差(多个独立环境)。
适用:管理后台、内部工具。腾讯 / 阿里早期都这么做。
2. 路由分发
nginx 按路径转发到不同独立应用:
location /billing/ {
proxy_pass http://billing-app.internal/;
}
location /users/ {
proxy_pass http://users-app.internal/;
}
本质是多个独立 SPA,用户在不同子应用间跳转。简单,但跨子应用切换会全量加载。
3. Single-SPA + qiankun
SPA 编排框架。主应用动态加载子应用资源,在同一个浏览器环境里运行多个子应用:
// 主应用注册子应用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react-app',
entry: '//react-app.example.com',
container: '#subapp',
activeRule: '/react'
},
{
name: 'vue-app',
entry: '//vue-app.example.com',
container: '#subapp',
activeRule: '/vue'
}
]);
start();
qiankun 提供 JS 沙箱(防止子应用全局变量污染)、样式隔离、生命周期、应用间通信。
优点:用户感受像 SPA,无刷新切换。
缺点:沙箱不是绝对安全,某些库(操作 document.body 的)会出问题。
4. Module Federation
Webpack 5 内置的功能,让一个应用运行时加载另一个应用的模块。最现代的微前端方案。
// host 应用 webpack.config.js
new ModuleFederationPlugin({
name: 'host',
remotes: {
users: 'users@http://localhost:3001/remoteEntry.js',
billing: 'billing@http://localhost:3002/remoteEntry.js'
}
})
// host 应用代码里直接 import
const UserList = lazy(() => import('users/UserList'));
const BillingPage = lazy(() => import('billing/BillingPage'));
// remote 应用 webpack.config.js
new ModuleFederationPlugin({
name: 'users',
filename: 'remoteEntry.js',
exposes: {
'./UserList': './src/UserList'
},
shared: ['react', 'react-dom'] // 共享依赖,避免重复加载
})
优点:细粒度共享(可以共享组件、hook、函数,不只是页面)、共享依赖避免重复加载、构建期解耦。
缺点:技术栈要相近(Module Federation 适合都用 Webpack 的项目)、运行时依赖能正常加载。
子应用之间通信
1. URL 状态
共享的状态走 URL query / hash,主应用监听变化。最简单。
2. EventBus
// 主应用提供全局事件总线
window.__bus = mitt();
// 任一子应用
window.__bus.emit('user-changed', userId);
window.__bus.on('user-changed', (userId) => { ... });
3. 共享状态(qiankun props)
// 主应用通过 props 传
{ activeRule: '/react', props: { user: currentUser } }
// 子应用接收
export async function mount(props) {
render(props.user);
}
样式隔离
多个子应用 CSS 同时存在,容易冲突。方案:
- CSS Modules / Scoped CSS:每个组件 class 自动加 hash 前缀,无冲突。
- Shadow DOM:浏览器级别隔离,最彻底,但调试复杂。
- 命名空间约定:每个子应用 CSS 都包在
.app-billing { ... }下,简单但要约束开发者。
共享依赖
5 个子应用都引用 React,bundle 里 React 出现 5 次,加载慢且内存浪费。解决:
- Module Federation shared:声明 React 共享,运行时只加载一份。
- External + UMD:把 React 当外部依赖,从 CDN 加载,所有子应用共用。
什么时候用微前端
- 团队规模 30+ 前端,分多个独立产品线。
- 不同产品技术栈不同且短期内无法统一。
- 需要独立部署、独立发布周期。
- 整合多个收购的独立产品。
什么时候不用
- 5-10 人小团队 —— 单体足够,微前端是过度设计。
- UI 高度一致、组件复用频繁的产品 —— 拆分反而增加复杂度。
- 没有运维 / 构建工具基础设施支持。
常见坑
坑 1:子应用过细。每个组件都做成独立子应用,运维复杂度爆炸。粒度应该是"业务模块",不是组件。
坑 2:全局状态依赖。子应用之间深度依赖共享状态,实际还是个大单体,只是物理分了。
坑 3:依赖版本不一致。子应用 A 用 React 17,B 用 React 18,共享有冲突。要么强制版本一致,要么各自携带(代价大)。
坑 4:加载失败处理。子应用资源服务挂了,主应用怎么显示?必须有 fallback UI 和重试。
坑 5:用户体验一致性。各子应用样式不同,用户感觉像在不同网站。需要共享设计系统。
替代方案:Monorepo
很多团队不需要"独立部署",只是想"代码隔离 + 独立开发" —— 这时 Monorepo(Turborepo / Nx / pnpm workspace)更简单:
- 一个仓库,多个 package,各自有 owner。
- 统一构建,统一部署。
- 共享代码方便,版本一致。
Monorepo 解决组织协作问题,微前端解决"必须独立部署"问题。选哪个看你真实需求。
写在最后
微前端不是"更先进的架构",是大型组织的妥协方案。如果你的团队 / 业务 / 技术债能容忍单体,单体永远更简单。引入微前端前,反复确认它解决的问题是不是你真正面临的问题。错误引入会让你后悔 —— 5 倍的复杂度,2 倍的运维负担,只有合适场景才值得。
一图看懂
微前端架构一图看懂:
—— 别看了 · 2026