微前端详解
一、微前端要解决什么问题
微前端(Micro-Frontends)是一种将前端应用拆分为多个独立子应用的架构模式,每个子应用可以独立开发、独立测试、独立部署,最终由主应用聚合为一个整体。
单体前端的问题
当一个前端项目增长到数十万行代码、上百个页面、几十人协作时,会出现以下问题:
- 构建缓慢:一次
npm run build耗时 5-10 分钟 - 部署耦合:修改一个 1KB 的文案需要重新构建部署整个巨大应用
- 技术栈锁定:项目早期选了 Angular 1,三年后想用 React 18,只能全量重写
- 团队冲突:多个团队在同一个 Git 仓库中修改代码,合并冲突频繁
- 回归风险:修改一个页面可能影响完全不相关的功能
微前端的解决思路
借鉴后端微服务的理念,将单体前端拆分为多个更小的子应用:
1 | ┌─────────────────────────────────────────────┐ |
核心特性:
| 特性 | 说明 |
|---|---|
| 技术栈无关 | 子应用可以用不同的框架(React/Vue/Angular/原生) |
| 独立开发 | 每个子应用有独立的仓库、CI/CD 流水线 |
| 独立部署 | 子应用可独立发布,不影响其他应用 |
| 增量升级 | 老系统不用重写,新功能用新技术栈实现 |
| 按需加载 | 用户只访问某个模块时,仅加载对应的子应用 |
二、技术方案对比
方案全景
| 方案 | 原理 | 样式隔离 | JS 沙箱 | 通信 | 生产案例 |
|---|---|---|---|---|---|
| iframe | 浏览器原生隔离 | ✅ 完全隔离 | ✅ 天然隔离 | postMessage | 几乎都在用 |
| Web Components | Custom Element | ✅ Shadow DOM | ❌ 无 | 事件/CustomEvent | YouTube |
| Module Federation | Webpack 5 插件 | ❌ 需自行处理 | ❌ 无 | 共享模块/全局变量 | 无数 |
| qiankun | single-spa + proxy | ✅ 实验性 | ✅ Proxy 沙箱 | 事件总线 | 阿里系 |
| micro-app | Custom Element 封装 | ✅ 自动隔离 | ✅ Proxy 沙箱 | 事件 + 数据 | 京东 |
| wujie(无界) | Web Component + iframe | ✅ | ✅ 双沙箱 | 事件 | 腾讯 |
1. iframe
优点:
- 浏览器级别的完全隔离(样式、DOM、JS 执行环境)
- 不需要任何改造,旧系统直接嵌入
缺点:
- URL 不同步:刷新 iframe 页面,浏览器前进后退无法控制
- 通信成本高:
postMessage传递复杂数据结构需要序列化 - 性能开销:每个 iframe 都有独立的执行环境,内存占用高
- 弹窗/遮罩层仅限于 iframe 内:全屏弹窗无法覆盖主应用
- SEO 不友好
适用场景:嵌入第三方不可控系统、遗留系统快速集成。
2. Web Components
使用 Custom Element + Shadow DOM 将子应用封装为自定义标签:
1 | <micro-app name="order" url="http://order-app.com"></micro-app> |
- Shadow DOM 提供天然的样式隔离
- Custom Element 提供生命周期(connectedCallback / disconnectedCallback)
- 但 JS 沙箱缺失,第三方库可能污染全局
3. qiankun(基于 single-spa)
qiankun 是蚂蚁金服开源的微前端框架,基于 single-spa 封装,是目前国内使用最广泛的方案。
1 | // 主应用 |
子应用需要导出生命周期钩子:
1 | // 子应用 entry |
qiankun 的核心机制
- 沙箱机制:使用
Proxy拦截 window 对象,子应用对 window 的修改在卸载时回滚 - 样式隔离:将子应用样式包裹在命名空间下(实验性),或使用 Shadow DOM
- 全局通信:
initGlobalState+onGlobalStateChange实现应用间通信 - 预加载:空闲时预加载子应用资源,提升切换体验
4. Module Federation(Webpack 5 / Rsbuild)
Module Federation 是 Webpack 5 内置的模块共享方案,不依赖运行时框架,通过构建时的模块联邦插件实现跨应用共享。
主应用配置:
1 | // webpack.config.js (主应用) |
子应用配置:
1 | // webpack.config.js (React 子应用) |
在主应用中加载远程模块:
1 | // 使用 React.lazy 动态加载远程组件 |
Module Federation 的优势
- 不依赖运行时框架,原生 Webpack 能力
- 共享依赖:多个子应用可以共享同一个 React 实例,避免重复加载
- 动态加载:支持运行时动态 remote
- 构建时集成 vs 运行时集成:MF 更偏向构建时集成,但也有运行时能力
Module Federation 2.0(Module Federation)
最新版本支持:
1 | // 动态加载 remote,无需预先配置 |
5. micro-app(京东)
micro-app 基于 Web Components + Proxy 沙箱,使用体验接近 iframe 的简单性:
1 | <!-- 主应用 --> |
1 | // 子应用适配(几乎零改造) |
特点:
- 使用 Custom Element 加载子应用
- Proxy 沙箱隔离 JS 作用域
- 自动拦截样式,通过添加属性选择器实现样式隔离
- 子应用改造极小
6. wujie 无界(腾讯)
wujie 结合了 iframe 的隔离能力和 Web Components 的便利性:
- DOM 放在 Web Component 中,样式互不影响
- JS 在 iframe 中运行,天然沙箱
- 路由同步通过
history.pushState代理
1 | <WujieVue |
方案选择建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 老系统 iframe 嵌入 | iframe | 零改造,快速集成 |
| 新项目整体架构 | Module Federation | 生态最完善,社区最活跃 |
| 已有 single-spa 项目 | qiankun | 平滑迁移,阿里生态 |
| 追求极简改造 | micro-app / wujie | 子应用改造量最小 |
| 跨技术栈组件共享 | Module Federation | 天然支持模块共享 |
三、核心挑战与解决方案
1. 样式隔离
方案一:CSS Modules / CSS-in-JS(子应用自治)
子应用各自使用 CSS Modules 或 styled-components,从构建层面避免冲突。这是最推荐的方案——不需要框架层面的样式隔离。
方案二:命名空间前缀(scoped css)
1 | /* 子应用所有类名加前缀 */ |
qiankun 的实验性样式隔离就是通过给样式表加前缀实现的。
方案三:Shadow DOM
真正的 DOM 隔离,但会导致:
- 弹窗/Select 下拉无法穿越 Shadow DOM 边界
- 第三方库如果向 body 挂载元素(如 Modal),样式会丢失
- 事件重定向问题
方案四:micro-app / wujie 自动隔离
通过拦截样式表插入过程,给每个 CSS 规则加上属性选择器 [data-micro-app-{name}]。
2. JS 沙箱
Proxy 沙箱(qiankun / micro-app)
1 | // 简化原理 |
Snapshot 沙箱(兼容 IE)
不支持 Proxy 时使用快照模式:
1 | // 激活时保存 window 快照 |
iframe 沙箱(wujie)
wujie 使用隐藏 iframe 运行 JS,天然隔离,但需要代理 postMessage 实现 DOM 操作同步,实现成本较高。
3. 应用间通信
方案一:全局事件总线
1 | // qiankun |
方案二:自定义事件
1 | // 发送方 |
方案三:共享模块(Module Federation)
通过 shared 配置共享状态管理库:
1 | // 主应用暴露共享 store |
4. 路由协调
主应用和子应用需要协同管理路由:
1 | URL: /order/list |
1 | // 主应用注册子应用 |
路由事件协调:子应用内部跳转时,主应用的侧边栏/面包屑需要同步高亮。
四、部署策略
独立部署(推荐)
每个子应用独立构建、独立部署到独立的 URL 路径:
1 | 主应用: https://app.example.com/ → base |
Nginx 配置:
1 | # 主应用 |
统一构建(不推荐)
所有子应用在同一流水线中构建,失去独立部署的优势。
版本管理
1 | // 主应用配置子应用版本 |
通过 CI/CD 环境变量注入:
1 | # .gitlab-ci.yml |
五、微前端 vs 其他方案
| 维度 | 微前端 | Monorepo | 独立多应用 |
|---|---|---|---|
| 开发隔离 | 高(独立仓库、独立构建) | 低(同一仓库) | 高 |
| 部署耦合 | 低 | 高(全量部署) | 无 |
| 技术栈 | 任意混用 | 统一 | 任意 |
| 代码复用 | 共享模块 | 直接引用 | 无 |
| 性能 | 按需加载 | 全量打包 | 页面跳转 |
| 适用场景 | 大型企业应用 | 中大型项目,技术栈统一 | 完全无关的系统 |
六、常见问题
Q1: 微前端是否适合所有项目
不适合。如果你的项目:
- 团队只有 1-3 人
- 代码量小于 10 万行
- 技术栈统一且没有遗留系统迁移需求
- 构建时间在 1 分钟以内
那么微前端带来的复杂度远超收益。
Q2: 样式冲突怎么排查
优先使用 CSS Modules / scoped styles 从子应用源头避免冲突。如果仍出现冲突:
1 | /* 使用浏览器 DevTools 确认冲突规则来自哪个文件 */ |
Q3: 子应用之间的公共依赖怎么处理
- Module Federation:通过
shared配置公共依赖,只加载一次 - qiankun:使用
externals将 React/Vue 抽离为全局依赖,子应用不打包框架 - 独立加载:每个子应用各自打包框架,CDN 缓存命中
1 | <!-- 主应用 HTML 中预加载公共库 --> |
Q4: 如何保证子应用之间不互相影响
- JS:使用沙箱隔离(Proxy / iframe)
- CSS:使用样式隔离(prefix / Shadow DOM)
- DOM:子应用只操作自己的容器节点
- 全局事件:统一管理,子应用卸载时清理 EventListener
Q5: 子应用公共入口加载失败怎么办
1 | // Module Federation 远程加载失败时的降级处理 |
七、推荐学习路径
- 先理解微前端的核心问题和主流方案的取舍(本文)
- 动手搭建一个 Module Federation 的 demo:一个 React 主应用 + 一个 Vue 子应用
- 尝试 qiankun 的官方示例,理解生命周期和沙箱原理
- 阅读 micro-app 或 wujie 源码中样式隔离和 JS 沙箱的实现
- 在真实项目中实践,关注性能指标(首屏加载时间、子应用切换耗时)
- 研究大厂案例:阿里 qiankun、腾讯 wujie、字节微前端方案