目录
-
- 一、什么是微前端
-
- [1.1 概念](#1.1 概念)
- [1.2 解决什么问题](#1.2 解决什么问题)
- [1.3 微前端 vs 微服务](#1.3 微前端 vs 微服务)
- 二、微前端的基本原理
-
- [2.1 四个基本命题](#2.1 四个基本命题)
- [2.2 集成时机:构建时 vs 运行时](#2.2 集成时机:构建时 vs 运行时)
- [2.3 常见实现流派](#2.3 常见实现流派)
- [2.4 四个命题的通用解法](#2.4 四个命题的通用解法)
- [三、qiankun 原理](#三、qiankun 原理)
-
- [2.1 整体定位:基于 single-spa 的封装](#2.1 整体定位:基于 single-spa 的封装)
- [2.2 路由劫持](#2.2 路由劫持)
- [2.3 HTML Entry:核心加载机制](#2.3 HTML Entry:核心加载机制)
- [2.4 JS 沙箱:隔离的核心](#2.4 JS 沙箱:隔离的核心)
- [2.5 CSS 隔离](#2.5 CSS 隔离)
- [2.6 生命周期](#2.6 生命周期)
- 四、实践案例:三技术栈混合微前端
-
- [3.1 整体架构](#3.1 整体架构)
- [3.2 基座实现](#3.2 基座实现)
- [3.3 子应用接入的通用改造](#3.3 子应用接入的通用改造)
- [3.4 各技术栈的差异](#3.4 各技术栈的差异)
- 五、踩坑记录
-
- [坑 1:`You need to export lifecycle functions`](#坑 1:
You need to export lifecycle functions) - [坑 2:`ReactDOM.render is not a function`](#坑 2:
ReactDOM.render is not a function) - [坑 3:`Failed to fetch`](#坑 3:
Failed to fetch) - [坑 4:Angular 版本与构建器兼容](#坑 4:Angular 版本与构建器兼容)
- [坑 5:zone.js 隔离问题](#坑 5:zone.js 隔离问题)
- [坑 1:`You need to export lifecycle functions`](#坑 1:
- 六、总结
- 参考
本文从「为什么需要微前端」讲起,梳理 qiankun 的核心原理,并结合一个真实项目------基座 + React + Vue + Angular 三技术栈混合的完整实践,包括踩过的坑与解决方案。

一、什么是微前端
1.1 概念
微前端(Micro Frontends)是把「微服务」的理念延伸到前端的一种架构风格:将一个庞大的前端应用拆分成多个可以独立开发、独立部署、独立运行的小应用,再由一个「基座」(主应用)把它们组合成一个整体呈现给用户。
用户看到的是一个完整的网站,背后其实是多个独立的前端应用在协同工作。
1.2 解决什么问题
传统单体前端(一个巨大的 SPA)随着业务膨胀会遇到:
- 技术栈锁定:整个项目绑死在某个框架/版本,想升级或换技术栈牵一发动全身
- 协作困难:几十人改同一个仓库,冲突多、构建慢、发布互相阻塞
- 历史包袱:老系统想接入新功能,只能用老技术栈重写
- 构建效率低:改一行代码,整个应用重新构建
微前端的价值:
| 能力 | 说明 |
|---|---|
| 技术栈无关 | React、Vue、Angular 可以共存,老系统也能接入 |
| 独立开发 | 各团队负责各自子应用,互不干扰 |
| 独立部署 | 子应用单独上线,不需要整体发布 |
| 增量升级 | 可以逐步替换老系统,而非推倒重来 |
1.3 微前端 vs 微服务
两者理念相通,但治理侧重点不同:
- 微服务治理:面向运行时的分布式系统,重点是网络通信可靠性(服务多、调用链长、故障传播)。
- 微前端治理 :面向浏览器里的集成运行,重点是隔离与协调(所有子应用跑在同一个浏览器 window 里,反而更容易互相干扰)。
微前端不需要微服务那套重型治理(注册中心、熔断、链路追踪),但有自己的命题:隔离、通信、共享依赖、统一监控。
二、微前端的基本原理
在讲具体框架之前,先理解微前端不依赖任何框架的通用原理:不管用什么方案,都要回答同样的四个问题,并在「集成时机」和「实现流派」上做选择。
2.1 四个基本命题
任何微前端方案,本质上都要解决这四件事:
| 命题 | 要解决什么 |
|---|---|
| 应用加载 | 如何把子应用的 HTML/JS/CSS 资源拿进来并运行 |
| 路由调度 | 如何根据 URL 决定当前显示哪个子应用 |
| 应用隔离 | 多个应用共存时,JS 全局变量、CSS 样式如何互不干扰 |
| 应用通信 | 基座与子应用、子应用之间如何传递数据 |
不同框架的差别,就在于用不同手段回答这四个问题。
2.2 集成时机:构建时 vs 运行时
构建时集成:子应用作为 npm 包 / 模块,被打包进主应用。
- 优点:共享依赖方便、类型友好
- 缺点:耦合紧,任一子应用更新都要重新构建发布主应用,失去「独立部署」
运行时集成 (主流):子应用独立部署,主应用在浏览器运行时动态加载它们。
- 优点:真正独立开发、独立部署
- 缺点:需要解决运行时的隔离与加载问题
- single-spa、qiankun 都属于这一类
2.3 常见实现流派
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| iframe | 每个子应用装进一个 iframe | 天然隔离(JS/CSS 完全独立) | 通信麻烦、路由/历史割裂、弹窗被困在 iframe 内、体验差 |
| Web Components | 用浏览器原生自定义元素封装子应用 | 标准化、原生隔离(Shadow DOM) | 框架适配成本高、生态不成熟 |
| 路由分发 + JS 加载(single-spa) | 主应用监听路由,动态加载子应用 JS 并调用其生命周期 | 灵活、体验好 | 隔离要自己处理,接入成本高 |
| HTML Entry 运行时加载(qiankun) | 在 single-spa 基础上,用 HTML 作入口 + 自动 JS/CSS 隔离 | 接入成本低、隔离开箱即用 | 需跨域 fetch,依赖沙箱 |
| Module Federation(webpack 5) | 构建/运行时共享模块,子应用暴露组件被远程加载 | 依赖共享强、粒度灵活 | 偏「模块共享」,需统一构建工具 |
| 服务端组合(SSI / 边缘拼装) | 在服务端或 CDN 边缘把多个 HTML 片段拼成一个页面 | 首屏快、SEO 友好 | 前端交互隔离弱,架构偏后端 |
2.4 四个命题的通用解法
- 应用加载:JS Entry(给 JS 文件)/ HTML Entry(给 HTML)/ Module Federation 的 remoteEntry / iframe 的 src
- 路由调度 :主应用统一劫持路由(
history/hashchange),按规则激活对应子应用;子应用用basename加前缀避免冲突 - 应用隔离 :
- JS:iframe 天然隔离 / Proxy 沙箱 / 快照沙箱
- CSS:iframe 天然隔离 / Shadow DOM / scoped 前缀 / BEM 命名约定
- 应用通信 :
props下发 / 全局状态(发布订阅)/ 自定义事件(EventBus)/ URL 参数
小结 :微前端的基本原理就是「运行时把多个独立应用加载到一个页面,并解决路由、隔离、通信」。下面要讲的 qiankun,正是「运行时集成 + HTML Entry + Proxy 沙箱」这条流派的成熟实现。
三、qiankun 原理
qiankun 是蚂蚁金服开源的微前端框架,是目前国内最主流的方案之一。它的原理可以拆成几块。
2.1 整体定位:基于 single-spa 的封装
qiankun 底层依赖 single-spa。single-spa 只解决「应用生命周期调度」------什么时候加载、挂载、卸载哪个应用;但需要手动处理子应用加载与 JS/CSS 隔离。
qiankun 在其之上补齐了三块能力:
single-spa → 生命周期调度(激活/卸载哪个应用)
+
qiankun → HTML加载 + JS沙箱 + CSS隔离 + 通信
2.2 路由劫持
start() 之后,qiankun 劫持浏览器路由变化(监听 hashchange、popstate,重写 pushState/replaceState)。每次 URL 变化时,用当前路径匹配每个子应用的 activeRule,匹配到就「加载→挂载」,离开时「卸载」。
2.3 HTML Entry:核心加载机制
这是 qiankun 相比 single-spa 最大的改进------用子应用的 HTML 地址作为入口,而不是 JS 文件。
js
entry: '//localhost:3000' // 给整个 HTML,而非 app.js
qiankun 通过 import-html-entry 库 fetch 这个 HTML,然后:
- 解析出所有
<script>、<link>、<style> - 把去掉 script 的 HTML 模板作为子应用 DOM 插入基座容器
- 逐个
fetchJS 并用eval执行 - 从执行结果中提取生命周期函数
好处是子应用几乎不用改打包配置,支持任意 chunk 拆分和按需加载;代价是需要跨域 fetch(子应用要配 CORS)。
2.4 JS 沙箱:隔离的核心
多个子应用共享同一个 window,容易互相污染(覆盖全局变量、遗留定时器、污染原型链)。沙箱让每个子应用感觉自己独占一个 window:
- Proxy 沙箱 (默认):用
Proxy给每个子应用代理一个「假 window」。写只写到隔离空间,不污染真实 window;读优先读自己的。 - 快照沙箱(老浏览器降级):挂载前给 window 拍快照,卸载时对比还原。
子应用卸载时,沙箱自动还原全局变量、清理定时器和事件监听,做到「切换时干干净净」。
2.5 CSS 隔离
experimentalStyleIsolation:给子应用样式选择器加属性前缀(类似 Vue scoped)strictStyleIsolation:把子应用塞进 Shadow DOM,物理隔离
2.6 生命周期
qiankun 通过子应用导出的三个函数控制它:
| 生命周期 | 时机 | 作用 |
|---|---|---|
bootstrap |
首次加载,只一次 | 初始化 |
mount |
每次激活 | 渲染应用到容器 |
unmount |
每次离开 | 销毁实例,防内存泄漏 |
四、实践案例:三技术栈混合微前端
下面是一个真实落地的项目:一个基座同时加载 React、Vue、Angular 三个子应用。

3.1 整体架构

┌─────────────────────────────────────────────┐
│ 基座应用 (localhost:9000) │
│ webpack + qiankun,注册与调度子应用 │
│ ┌─────────┬──────────┬──────────────┐ │
│ ▼ ▼ ▼ │
│ /app-react /app-vue /app-angular │
└───┼─────────┼──────────┼──────────────────────┘
▼ ▼ ▼
React Vue Angular
:3000 :8080 :4200
| 应用 | 技术栈 | 端口 | 路由 |
|---|---|---|---|
| 基座 | webpack 5 + qiankun 2.10 | 9000 | --- |
| React 子应用 | React + react-router-dom v5 (CRA) | 3000 | /app-react |
| Vue 子应用 | Vue 2.6 + vue-router 3 + vuex 3 (Vue CLI) | 8080 | /app-vue |
| Angular 子应用 | Angular + custom-webpack (NgModule) | 4200 | /app-angular |
目录结构
bash
micro-frontend/
├── src/ # 基座应用源码
│ ├── index.js # 注册子应用 + start()(含 zone.js 全局引入)
│ └── index.html # 基座页面骨架(导航 + #container 容器)
├── webpack.config.js # 基座 webpack 配置(HtmlWebpackPlugin 自动注入 JS)
│
├── react-app/ # React 子应用 (CRA)
│ └── src/
│ ├── public-path.js # qiankun 动态 publicPath
│ ├── index.js # 生命周期挂到 window(React 18+ createRoot)
│ ├── App.js # react-router-dom v5 路由
│ └── setupProxy.js # 开发服务器 CORS
│
├── vue-app/ # Vue 2.x 子应用 (Vue CLI)
│ ├── src/
│ │ ├── public-path.js
│ │ ├── main.js # export 生命周期,render 中重建 router
│ │ └── router/index.js # 导出 routes 数组
│ └── vue.config.js # umd 打包 + CORS + 8080 端口
│
└── angular-app/ # Angular 子应用 (Angular CLI)
├── src/
│ ├── public-path.ts
│ ├── main.ts # export 生命周期
│ ├── index.html # app-root 唯一 id + zone.js CDN 兜底
│ └── app/ # APP_BASE_HREF 设路由 base,selector 加唯一前缀
├── custom-webpack.config.js # umd 打包 + CORS
└── angular.json # 切换到 custom-webpack builder
3.2 基座实现
基座本身是一个简单的 webpack 应用,核心是注册子应用并启动:
js
import 'zone.js'; // Angular 子应用依赖,必须在 qiankun 之前全局引入
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{ name: 'reactApp', entry: '//localhost:3000', container: '#container', activeRule: '/app-react' },
{ name: 'vueApp', entry: '//localhost:8080', container: '#container', activeRule: '/app-vue' },
{ name: 'angularApp', entry: '//localhost:4200', container: '#container', activeRule: '/app-angular' },
]);
// 关闭 prefetch,避免预请求尚未启动的子应用导致 Failed to fetch
start({ prefetch: false });
3.3 子应用接入的通用改造
无论哪个技术栈,接入 qiankun 都要做这几件事:
public-path:qiankun 注入__INJECTED_PUBLIC_PATH_BY_QIANKUN__,让子应用资源从自己的域名加载- 导出生命周期 :
bootstrap/mount/unmount - 路由 base :qiankun 加载时用
/app-xxx作为路由前缀,独立运行时用/ - DOM 隔离:限制根节点查找范围,避免与其他应用冲突
- CORS :开发服务器设置
Access-Control-Allow-Origin: *
3.4 各技术栈的差异
| 项 | React | Vue | Angular |
|---|---|---|---|
| 生命周期导出 | 挂到 window(CRA 无法改打包格式) |
export + UMD 打包 |
export + UMD 打包 |
| 打包格式 | CRA 默认 | vue.config.js 设 UMD |
custom-webpack 设 UMD |
| 路由 base | BrowserRouter basename |
VueRouter base |
APP_BASE_HREF |
| DOM 隔离 | 限定 #root |
限定 #app |
app-root 加唯一 id |
五、踩坑记录
实践中遇到的报错,恰好印证了 qiankun 的原理。
坑 1:You need to export lifecycle functions
现象:点击子应用报错,找不到生命周期函数。
原因 :qiankun 用 eval() 执行子应用 JS,普通的 ES Module export 在 eval 作用域里拿不到。
解决:
- React(CRA 不能改打包格式)→ 把生命周期挂到
window.reactApp = { bootstrap, mount, unmount } - Vue / Angular → 打包成 UMD (
libraryTarget: 'umd'),UMD 会把导出挂到全局
坑 2:ReactDOM.render is not a function
现象:React 子应用挂载报错。
原因 :教程用的是 React 16 的 ReactDOM.render,而 CRA 实际装的是 React 18+,该 API 已移除。
解决 :改用 ReactDOM.createRoot(el).render(...),卸载用 root.unmount()。
坑 3:Failed to fetch
现象:加载子应用时网络请求失败。
原因 :start() 默认 prefetch: true,会预加载所有注册的子应用 HTML。未启动的 Vue/Angular 被预请求,fetch 失败。
解决 :start({ prefetch: false }),只在真正进入子应用时才加载。
坑 4:Angular 版本与构建器兼容
现象 :最新 Angular 默认用 esbuild 的 application 构建器,不支持自定义 webpack;而 qiankun 需要 webpack 的 UMD 输出。
解决 :安装匹配版本的 @angular-builders/custom-webpack,把 angular.json 的 builder 从 @angular/build:application 切回 webpack 的 browser 构建器,再配 UMD。
坑 5:zone.js 隔离问题
现象:Angular 依赖的 zone.js 若打包进子应用,在沙箱里可能 patch 到「假 window」出问题。
解决 :zone.js 移到基座全局引入(且在 import qiankun 之前);Angular 子应用独立运行时用 index.html 的 CDN 脚本(带 ignore 属性,被 qiankun 加载时跳过)兜底。
六、总结
- 微前端解决单体前端的技术栈锁定、协作困难、增量升级问题,核心价值是「独立开发、独立部署、技术栈无关」。
- qiankun 原理:single-spa 生命周期调度 + HTML Entry 直接加载子应用整站 + Proxy 沙箱做 JS 隔离 + 前缀/Shadow DOM 做 CSS 隔离。
- 实践关键:每个子应用做好 public-path、生命周期导出、路由 base、DOM 隔离、CORS 五件事;不同技术栈在「生命周期导出方式」和「打包格式」上有差异。
- 治理演进:入门用静态注册即可;走向生产时按「隔离 → 错误兜底 → 鉴权/监控 → 应用注册中心 → 共享依赖」的优先级逐步补齐。
微前端不是银弹------小项目用它反而增加复杂度。但当团队规模、系统体量、技术栈异构达到一定程度时,它是让前端架构可持续演进的有效手段。
参考
- qiankun 官方文档:https://qiankun.umijs.org/
- single-spa:https://single-spa.js.org/