微前端实践:从概念、原理到 qiankun 落地

目录

    • 一、什么是微前端
      • [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 隔离问题)
    • 六、总结
    • 参考

本文从「为什么需要微前端」讲起,梳理 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 劫持浏览器路由变化(监听 hashchangepopstate,重写 pushState/replaceState)。每次 URL 变化时,用当前路径匹配每个子应用的 activeRule,匹配到就「加载→挂载」,离开时「卸载」。

2.3 HTML Entry:核心加载机制

这是 qiankun 相比 single-spa 最大的改进------用子应用的 HTML 地址作为入口,而不是 JS 文件。

js 复制代码
entry: '//localhost:3000'   // 给整个 HTML,而非 app.js

qiankun 通过 import-html-entryfetch 这个 HTML,然后:

  1. 解析出所有 <script><link><style>
  2. 把去掉 script 的 HTML 模板作为子应用 DOM 插入基座容器
  3. 逐个 fetch JS 并用 eval 执行
  4. 从执行结果中提取生命周期函数

好处是子应用几乎不用改打包配置,支持任意 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 都要做这几件事:

  1. public-path :qiankun 注入 __INJECTED_PUBLIC_PATH_BY_QIANKUN__,让子应用资源从自己的域名加载
  2. 导出生命周期bootstrap / mount / unmount
  3. 路由 base :qiankun 加载时用 /app-xxx 作为路由前缀,独立运行时用 /
  4. DOM 隔离:限制根节点查找范围,避免与其他应用冲突
  5. 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 → 打包成 UMDlibraryTarget: '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 五件事;不同技术栈在「生命周期导出方式」和「打包格式」上有差异。
  • 治理演进:入门用静态注册即可;走向生产时按「隔离 → 错误兜底 → 鉴权/监控 → 应用注册中心 → 共享依赖」的优先级逐步补齐。

微前端不是银弹------小项目用它反而增加复杂度。但当团队规模、系统体量、技术栈异构达到一定程度时,它是让前端架构可持续演进的有效手段。

参考

相关推荐
并红塔1 小时前
AI Agent 多智能体编排架构实战:从PoC到事件驱动的架构演进与踩坑全记录
人工智能
Cloudtechnology1 小时前
GEO服务商哪家好?2026年头部服务商技术路线、行业适配与ROI数据全解析
大数据·人工智能
集之互动1 小时前
坚守合规传播底线 集之互动AI TVC重构大健康品牌精细化传播体系
人工智能·重构
动物园猫1 小时前
钢材表面缺陷目标检测数据集:6类别 | 目标检测
人工智能·目标检测·计算机视觉
用户059540174461 小时前
AI记忆存储一致性踩坑实录:每天手工回归200条对话,直到我用pytest+Redis搭建自动化验证
前端·css
weedsfly1 小时前
前端中的适配器模式 vs 外观模式
前端·javascript·面试
图扑软件1 小时前
其域创新 × 图扑软件|3DGS 高斯泼溅智慧钢厂实景孪生
前端·低代码·3d·编辑器·图形渲染·可视化
labixiong1 小时前
写给前端的 Generator 指南:从 yield 到 async/await 的完整进化
前端·javascript·前端框架
gis开发之家1 小时前
《Vue3 从入门到大神32篇》Vue3 源码详解(二):从 GitHub Clone 到断点调试,手把手搭建源码阅读环境
vue.js·前端框架·github·vue3·vue3源码