万字长文带你了解微前端架构

引言:前端架构演进与挑战

区别传统的静态网站,现在的各种 Web 应用被称为富互联网应用。在线服务的激增使我们能够方便地观看喜欢的电影和直播、即时订购外卖以及各种生活便利。

然而这样的技术实现往往会把功能都集中在一个项目当中,随着模块越来越多,这种「单体应用」就面临了许多难题:

  • 多人协作困难:多个开发团队在同一个代码仓库中协作,容易产生代码冲突、依赖冲突。

  • 代码复杂度高:项目膨胀,维护成本上升,回归测试压力变大。

  • 版本升级困难:一个模块的小变动可能影响整个应用,发布频率受到限制。

为了解决这样的问题,后端提出了「微服务架构」的解决方案。而在前端,同样演进出了对应的方向------微前端


一、什么是微前端?

1.1 微前端的定义

微前端是受微服务启发的一种新兴架构。它背后的主要思想是将一个单体代码库分解成多个较小的部分,以便多个相对独立的团队进行分工协作,实现了复杂业务解耦独立部署团队自治 。不仅适用于 monorepo ,也适用于 polyrepo

1.2 微前端的发展背景

微前端并不是一开始就存在的,而是伴随以下几个变化逐步发展出来的:

  • 公司业务越来越复杂,单一前端团队无法高效支撑;
  • 项目重构或多个产品线需要共存,但技术栈不统一;
  • 希望多个子系统可以独立部署、独立升级;
  • 前后端分离普及后,前端层也需要更细的拆分和治理。

1.3 适合微前端的业务类型

适合场景 说明
多产品线并存的后台管理系统 每个产品线可作为子应用独立开发和部署
技术栈混用(React、Vue、Angular) 允许不同技术栈共存
渐进式重构旧系统 可逐步替换旧模块为新微前端模块
多团队协作开发大型项目 团队边界清晰,职责明确

1.4 应用微前端

在应用微前端之前需要考虑以下因素:

  • 业务领域描述
  • 自治代码库
  • 独立部署
  • 单一团队负责

在确定应用微前端架构以后,需要确定如何从技术视角考虑微前端,并且需要决定是在同一个视图中集成多个微前端,还是每个视图中集成一个微前端,即如图所示横向拆分和纵向拆分。

在横向拆分方案中,同一个视图上会有多个微前端。在出现以下几种情况时效果比较好:

  • 当子业务在多个视图中渲染时,子业务的复用性成为业务关键
  • 当SEO时项目的必选项,需要使用服务端渲染时
  • 当前端项目至少几十人协同开发,不得不把项目拆分成多个子域
  • 当面临一个定制化的多租户项目时

而在纵向拆分方案中,每个团队负责一个业务领域。当项目需要一致的用户界面变化和跨页面的流畅的用户体验时,纵向拆分显得非常有用。对于 SPA 来说,纵向拆分方式的开发体验最为契合。纵向拆分中 App shell 利用全局路由来加载不同的微前端。

下图显示传统的微服务架构,其前端整体结构使用 API 网关连接到后端微服务。

下图显示了具有不同微服务实现方式的微前端架构。

1.5 微前端实践原则

Sam Newman在《微服务设计》中强调了一些微服务的原则,看看如何应用到微前端。

  1. 围绕业务领域建模:弄清如何分治应用有利于后期添加新功能
  2. 自动化文化:确保稳固的持续集成、流水线部署以及快速反馈循环
  3. 隐藏实现细节:每个团队不需要依赖外部,专注于内部的实现细节
  4. 分布式治理:分散团队的决策权,在特定领域之内的操作不需要等待领导决策
  5. 独立部署:每个团队按照自己的计划独立部署应用
  6. 故障隔离:必须提供替代的内容或者隐藏应用的对应部分
  7. 高度可观察性:前端监控变得日益重要,投入资源准备随时解决故障而非完全预防故障

二、微前端核心原理

2.1 主子应用的架构模型

微前端架构采用一种类似"门户 + 插件"的结构,将整个页面按照职责边界划分成主应用和多个子应用。

  • 主应用(Host App / Shell)

    • 负责整体框架结构的搭建,包括:

      • 全局路由管理
      • 公共组件(导航栏、菜单、权限控制等)
      • 子应用的注册、加载、卸载
      • 应用间通信调度器
  • 子应用(Sub App / Micro App)

    • 每个子应用是一个独立的 SPA 或 MPA,可以:

      • 拥有自己的路由体系
      • 使用独立的技术栈(React/Vue/Angular)
      • 独立构建、测试和部署
      • 在运行时被主应用动态挂载

一个类比:

可以把主应用看作"容器",子应用看作"插件模块"。主应用控制"何时加载哪个模块",而模块本身专注于自己的逻辑实现。

graph TD A[基座应用] --> B{路由分发器} B -->|匹配 /order| C[订单微应用] B -->|匹配 /user| D[用户中心微应用] B -->|匹配 /dashboard| E[数据看板微应用] A --> F[共享依赖库] C --> F D --> F E --> F A --> G[通信总线] C -->|事件发布| G D -->|事件监听| G

2.2 核心技术原理

微前端架构的最大魔力在于:它让多个独立开发、构建、部署的前端项目,看起来像是一个统一的网页应用。用户完全感知不到页面是在多个代码库之间跳转,开发者也可以实现子应用的独立演进。

那么,从技术上来说,微前端是怎么把一个子应用"嵌入"主应用中的呢?

2.2.1 子应用的基本状态:本质是一个远程资源包

从主应用角度看,每一个子应用就是一个远程构建好的静态资源包,可能部署在如下路径:

js 复制代码
https://static.example.com/sub-app-user/
  ├── index.html
  ├── main.js
  ├── styles.css

这个资源包含了子应用的HTML模板、JS入口文件、CSS样式文件。

2.2.2 主应用挂载子应用的流程

主应用并不会将子应用打包进来,而是通过配置动态加载远程子应用,过程大致如下:

步骤1:注册子应用

在主应用中注册子应用的信息:

ts 复制代码
registerMicroApps([
  {
    name: 'userApp',
    // 子应用的资源入口
    entry: 'https://static.example.com/sub-app-user/',
    // 渲染子应用的 DOM 容器
    container: '#subapp-viewport',
    // 子应用激活的路由规则
    activeRule: '/user',
  }
]);
步骤2:当路由命中时,主应用动态加载子应用资源

主应用拦截到当前路由为 /user 后,会执行如下操作:

  1. 通过 fetchiframe 等方式加载子应用入口页面(如 index.html
  2. 解析该 HTML 文件中需要加载的 JS、CSS
  3. 插入 <script><link> 标签,将子应用的资源动态注入主应用页面中
  4. 查找子应用打包暴露的全局对象(如 window['userApp'])或模块导出,执行其 mount() 函数
步骤3:主应用调用子应用的 mount(),渲染子应用

一旦子应用的 JS 加载完毕,就会调用其生命周期:

js 复制代码
window['userApp'].mount({
  container: document.querySelector('#subapp-viewport'),
  props: { fromMainApp: true }
});

此时:

  • 子应用会将自己的路由系统、组件、页面渲染到主应用提供的 container
  • 子应用像一个"沙箱应用"一样运行在主页面里
步骤4:切换路由时调用 unmount()

当用户跳转到另一个子应用(如 /order)时,主应用会调用当前子应用的 unmount() 方法,做清理工作(如移除事件监听、清空 DOM 节点等),然后加载并挂载新的子应用。

2.2.3 从用户视角看:为何"无感知"?

这套机制之所以能实现"用户无感"的体验,核心在于:

  • 页面不刷新,子应用是局部加载并渲染到主页面中
  • 主应用统一管理路由、导航栏等公共区域,切换时不丢失上下文
  • 各个子应用在同一个 URL 路径规则下工作,地址栏保持统一
  • 主子应用共享样式、组件、状态(如通过主应用传递 props)

因此,从用户视角来看,整个网页就像是一个完整的一体化系统,但背后其实是多个不同项目在协作运行。

2.3 微前端集成方案

微前端的核心目标是让多个独立前端项目协同工作 ,而"集成方案"指的就是:主应用如何加载、渲染并控制子应用的运行

2.3.1 iframe集成

原理 :主应用通过 iframe 加载子应用的页面,每个子应用运行在自己的浏览器上下文中。

js 复制代码
<iframe src="https://sub-app.example.com" />
2.3.2 运行时HTML插入+JS挂载

原理

  • 主应用在运行时,通过 AJAX 或 <script> 加载子应用的资源(HTML/JS/CSS);
  • 将子应用渲染到主应用指定容器;
  • 调用子应用暴露的生命周期函数:bootstrap → mount → unmount
js 复制代码
registerMicroApps([
  {
    name: 'orderApp',
    entry: 'https://order.example.com/',
    container: '#subapp-container',
    activeRule: '/order',
  },
]);
2.3.3 Webpack Module Federation(构建时集成)

原理

  • 在构建阶段声明共享模块(remote、exposes);
  • 主应用可以像本地模块一样引入子应用暴露的内容;
  • 支持模块按需加载,避免冗余依赖
js 复制代码
// 主应用 Webpack config
remotes: {
  userApp: 'userApp@https://cdn.example.com/remoteEntry.js'
}
2.3.4 基于 Web Components

Web Components 是浏览器原生支持的一组规范,包括:

  • Custom Elements:自定义标签,如 <user-app></user-app>
  • Shadow DOM:为组件提供样式和 DOM 隔离
  • HTML Templates:定义可复用的 HTML 结构

在微前端中,我们可以将每个子应用封装成一个 Web Component,然后在主应用中直接使用自定义标签进行挂载。

js 复制代码
<script src="https://cdn.example.com/user-app/main.js"></script>
<user-app></user-app>

2.4 总结

实现方式 集成时机 是否支持异构 隔离性 性能 通信复杂度 实现复杂度
iframe 运行时 ⭐⭐⭐⭐⭐(进程级) ❌ 慢 ⭐⭐⭐⭐(需 postMessage) ⭐(非常简单)
HTML 插入 + JS 挂载 运行时 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐(eventBus/props) ⭐⭐⭐⭐
Webpack Module Federation 构建时 ❌(需统一工具链) ⭐⭐(无沙箱) ⭐⭐⭐⭐⭐(最佳) ⭐(共享模块) ⭐⭐⭐⭐⭐
Web Components 运行时 ✅(原生支持) ⭐⭐⭐⭐⭐(Shadow DOM) ⭐⭐⭐⭐ ⭐⭐(自定义事件/props) ⭐⭐(需封装生命周期)

三、主流微前端框架对比

目前,微前端领域已涌现出多种实现方案和框架。以下介绍四种具有代表性的微前端框架,它们代表了不同的实现范式适用场景

3.1 Single-SPA

3.1.1 框架发展历程

Single-SPA 是最早定义"微前端"形态的框架之一,由 Canopy 公司在 2016 年开源,首次将主-子应用模型抽象为标准化的生命周期机制,并且允许不同技术栈的子应用共存(React、Vue、Angular 等)。

该框架目前已进入稳定维护期,是许多微前端框架的理论基础(包括 qiankun、wujie)。

3.1.2 实现原理概述

Single-SPA 并不负责子应用资源加载,而是提供运行时框架与生命周期调度机制

  • 统一注册子应用信息(基于路由)
  • 在路由切换时执行子应用的 bootstrap → mount → unmount
  • 通过 SystemJS 或开发者自定义方式加载子应用 JS 入口
  • 不强制样式隔离、沙箱机制,需自行集成
3.1.3 优点
  • 技术中立:支持多技术栈共存(React/Vue/Angular)
  • 生命周期清晰:微前端应用运行流程全流程掌控
  • 社区成熟:作为微前端概念提出者,架构思想广泛应用
  • 插件化强:与 SystemJS 等组合更灵活
3.1.4 缺点
  • 较低层:需要自己处理资源加载、样式隔离、通信机制
  • 学习成本略高,开发体验不如 qiankun 等"全家桶"
  • 子应用需主动适配生命周期(非零侵入)

3.2 qiankun

3.2.1 框架发展历程

qiankun 是由阿里巴巴旗下 Ant Financial 团队于 2019 年开源,构建于 Single-SPA 之上,但进一步实现了运行时资源加载、样式隔离、沙箱机制、通信机制等一整套完整解决方案,被誉为"企业级微前端最佳实践"。

目前在国内外都有广泛使用(如阿里云、钉钉、支付宝)。

3.2.2 实现原理概述

qiankun 的核心技术架构:

  • 构建于 single-spa 之上(生命周期调度)
  • 使用 HTML Entry 加载子应用资源(HTML + JS + CSS)
  • 使用 沙箱(Proxy + Snapshot) 实现全局变量隔离
  • 使用 CSS Scope + strict style isolation 处理样式冲突
  • 提供 initGlobalState() 等通信机制(基于发布-订阅)

主应用注册子应用示例:

ts 复制代码
registerMicroApps([
  {
    name: 'userApp',
    entry: 'https://cdn.xxx.com/user/',
    container: '#container',
    activeRule: '/user',
  }
]);
3.2.3 优点
  • 开箱即用:涵盖资源加载、生命周期、样式隔离、通信等
  • 兼容技术栈:支持 Vue、React、Angular、纯 HTML
  • 开发体验好:主子应用接入成本低,文档完善
  • 支持 prefetch 预加载机制
3.2.4 缺点
  • 内部实现复杂(调试较困难)
  • 非全局沙箱(不适合高安全要求的系统)
  • 不适合 SSR 场景(浏览器为核心)

3.3 wujie

3.3.1 框架发展历程

Wujie 是腾讯开源的一款轻量级微前端框架,诞生于 2022 年,目标是解决 qiankun 在性能、安全、加载机制方面的部分痛点。

Wujie 使用原生 iframe 嵌套 +动态补丁挂载方式,实现近乎原生性能的微前端架构。

3.3.2 实现原理概述
  • 每个子应用通过 iframe 承载(避免全局变量污染)
  • 使用原生浏览器机制进行沙箱和样式隔离(借助 Shadow DOM)
  • 启动阶段将子应用 DOM 迁移到主页面中(DOM Patch)
  • 提供主子通信、生命周期调度能力

Wujie 使用示例:

ts 复制代码
<wujie-vue
  name="user"
  url="https://user-app.cdn.com"
  sync
  alive
></wujie-vue>
3.3.3 优点
  • 高性能:相比 qiankun 启动更快(DOM patch 优化)
  • 真正隔离:基于 iframe,避免全局污染
  • 轻量实现:不依赖第三方调度框架
  • 支持 keep-alive、DOM 缓存
3.3.4 缺点
  • 运行机制复杂,调试门槛略高
  • 使用 iframe 带来部分兼容性风险
  • 缺少 qiankun 社区活跃度

3.4 module-federation

3.4.1 框架发展历程

Webpack Module Federation 是 Webpack 5 在 2020 年引入的新特性,支持多个独立构建的项目共享模块、组件、逻辑,实现构建时的微前端

它并不是微前端框架,但被广泛应用于构建类 monorepo 的大型项目中。

3.4.2 实现原理概述
  • 每个子应用通过 Webpack 配置 exposesremotes
  • 主应用在运行时加载远程模块(不再是 HTML,而是 JS Module)
  • 自动管理共享依赖(如 React、Vue),避免重复打包
js 复制代码
// 主应用
remotes: {
  userApp: 'userApp@https://cdn.example.com/remoteEntry.js'
}

子应用暴露模块:

js 复制代码
exposes: {
  './UserPage': './src/pages/UserPage.vue',
}
3.4.3 优点
  • 性能极佳:构建时优化,打包依赖最小
  • 模块级复用:可共享组件、hook、工具库等
  • 自动依赖去重:支持 singleton 配置
3.4.4 缺点
  • 仅适用于 Webpack 5(Vite、Rollup 不支持)
  • 配置复杂,调试成本高
  • 不适用于技术栈异构或运行时动态加载场景
  • 不提供生命周期、沙箱、通信机制(需自行实现)

四、微前端常见挑战

虽然微前端架构在工程上带来了灵活性与模块化,但也引入了许多"从单体应用中从未遇到"的复杂问题。

4.1 微前端的组合

微前端架构下,每个子应用由独立团队开发、部署,最终需要在主应用中"拼接"为一个整体用户体验。这个过程就是"组合"(Composition)。

  • 如何定义主应用与子应用的分工边界?
  • 子应用如何以模块化方式挂载?
  • 如何避免主子应用之间强耦合?
常见解决方案
  • 统一规范接口 :主应用通过配置(如 nameentrycontaineractiveRule)管理子应用挂载
  • 生命周期抽象 :采用 single-spa、qiankun 等框架定义 bootstrapmountunmount
  • 可视化组合平台:部分大厂通过后台平台配置哪些子应用加载到哪些容器

4.2 微前端的路由

传统 SPA 由一个框架控制整个路由系统,而微前端场景中,主子应用都需要路由,各自有自己的页面、跳转逻辑。

  • 主子应用路由如何解耦?
  • 子应用如何感知当前 URL?
  • 如何避免路由冲突或丢失?
常见解决方案
  • 嵌套路由机制 :主应用控制整体路由前缀(如 /user/*),子应用控制自身内部路由
  • hash 路由隔离 :主、子应用分别使用 historyhash 路由(避免路由冲突)
  • 统一监听机制:通过主应用统一监听 URL 变化并通知子应用(如 qiankun 自动处理)

4.3 微前端的状态管理

当用户跨子应用跳转(例如从"订单模块"跳转到"个人中心"),有些状态是全局共享的,比如登录信息、主题设置、语言等。

  • 状态在哪一层维护?主应用?子应用?
  • 子应用之间如何共享状态?
  • 如何避免状态污染或丢失?
常见解决方案
  • 主应用持有全局状态:如登录态、权限信息,由主应用下发
  • 通过通信桥接共享状态 :例如 qiankun 的 initGlobalState 提供了响应式共享状态
  • 封装状态管理插件 :如使用 redux-subspacezustand context bridge 等专门适配微前端的方案

4.4 微前端之间的通信

微前端系统中,多个子应用由不同团队维护,需要在运行时进行事件通知或数据交互。例如,商品模块通知购物车模块"商品已加入"。

  • 主子、子子通信通道如何建立?
  • 事件如何订阅/解绑?生命周期如何管理?
  • 通信协议是否标准化?
常见解决方案
  • 事件总线机制 :主应用提供全局 EventBus(可封装在 window 或 shared module)
  • 框架自带通信系统 :如 qiankun 的 initGlobalState 提供发布-订阅模型
  • 使用浏览器原生通信机制 :如 CustomEventwindow.postMessage(iframe 场景)

4.5 微前端之间的样式隔离

多个子应用渲染到同一页面中时,CSS 极易发生污染,比如全局样式冲突、类名重叠、第三方组件样式覆盖等。

  • 子应用样式是否会污染主应用?
  • 如何防止多个子应用样式互相干扰?
  • 如何保证主题风格一致的同时避免全局污染?
常见解决方案
  • 命名空间隔离:使用 CSS Modules、BEM、Scoped CSS 避免类名冲突
  • Shadow DOM:Web Components / Wujie 默认支持样式隔离
  • 运行时样式处理 :如 qiankun 提供 strictStyleIsolationscopedCSS 配置,将样式作用域限定在容器内

参考资料

  1. AWS规范性指导-了解和实现微前端
  2. 《微前端设计与实现》------卢卡 梅扎利拉
  3. 领域驱动设计------Eric Evans
相关推荐
灿灿1213812 分钟前
CSS 文字浮雕效果:巧用 text-shadow 实现 3D 立体文字
前端·css
烛阴30 分钟前
Babel 完全上手指南:从零开始解锁现代 JavaScript 开发的超能力!
前端·javascript
AntBlack1 小时前
拖了五个月 ,不当韭菜体验版算是正式发布了
前端·后端·python
31535669131 小时前
一个简单的脚本,让pdf开启夜间模式
前端·后端
尘心cx1 小时前
前端-CSS-day1
前端·css
知否技术1 小时前
前端常说的 SCSS是个啥玩意?一篇文章给你讲的明明白白!
前端·scss
幼儿园技术家1 小时前
Uniapp简易使用canvas绘制分享海报
前端
开开心心就好2 小时前
免费PDF处理软件,支持多种操作
运维·服务器·前端·spring boot·智能手机·pdf·电脑
全宝2 小时前
🎨前端实现文字渐变的三种方式
前端·javascript·css
yanlele3 小时前
前端面试第 75 期 - 2025.07.06 更新前端面试问题总结(12道题)
前端·javascript·面试