
引言
我自己经历了两个团队,它们都有业务有着非常相似的业务,其中一个采用了微前端架构,一个没有采用前端架构,正是因为我亲身感受过这两种不同的架构模式,所以我对这两种模式有着深切的体会,我想结合我自己的感受,以及我对微前端的了解,来谈谈我们为什么需要微前端。
内容比较长,没时间看的同学可以先收藏起来 慢慢看
什么是大前端?
大前端指的是前端开发的职责和技术范围的扩展,不仅局限于浏览器端开发,涵盖了整个前端技术体系,强调跨平台开发和统一管理。
技术栈角度:
- Web 前端:PC 网站、移动 H5 页面
- 跨端开发:Hybrid 应用、React Native、Flutter、小程序
- 桌面端:Electron 等桌面应用
- 后端相关:Node.js 服务、BFF 层
- 基础设施与运维:性能优化、工具链、前端监控
开发流程:
大前端不仅负责业务代码开发,还涉及构建、DevOps、性能优化等环节。例如,客户端任务可以转移到构建阶段(如SSG),或通过SSR将任务移至服务器,结合容器技术定制应用环境,实现更高的灵活性。
业务角度:
大前端团队负责同一C端产品的全业务线开发,尽管团队内部可按业务划分小组,每个小组专注特定业务。比如,差旅平台的大前端团队 负责机票、酒店、火车等各项系统的开发,并承担公共内容(如UI组件库、请求库等)的架构管理。
背景
为了方便我们后续讨论,假设我们的业务背景是开发一个行程预定APP,包含机票、酒店、火车票等业务,每个业务又包含了列表页、下单页、订单详情以及售后页面。首页是我们各个业务的入口承担搜索功能。

通常情况下我们有以下几种架构可以选择Architecture
单应用架构 (Single-App Architecture)
最简单的我们可以将首页、机票搜索、下单页、支付、订单详情,全部在同一个项目中,那他们的组件在同一个项目中,可以实现共享,如果你采用SPA模式跳转页不需要重新刷新页面,所以用户看不到白屏,甚至我们可以利用过渡动画和keepalive,预加载,预请求等技术给用户流畅的用户体验。
场景:
有两个需求,一个是机票业务,另一个需求是修改火车票业务,明明这两个需求并不相关,但是我们在一个代码库中,合并代码发布代码我们势必要对齐,一个需求回退势必会导致另一个需求的回退,要么我们选择先后顺序发布,无论是哪种方式它们之间产生了影响,不同业务的人在同一个代码库中操作久而久之会越来混乱。
这只是一个简单的案例,真实案例中这种需求成百上千,开发人员和管理团队都需要很大的精力来分析并且处理这些事物。大大的增加了开发人员的沟通成本,也容易让业务之间相互影响。
上述案例只是为了说明架构的演进,我相信很多人会直接跳到后面的架构而不是真的会去采用单应用架构架构,如果一个业务相对比较简单。那么我们采用上述架构完全没有问题,在后续业务迭代的时候再新增应用 变成多应用架构
我们这里的Single-App Architecture 和 SPA 有所区别 SPA 指的是单页应用,强调的是一个应用只有一个页面,只会加载一次html,后续都是通过js实现虚拟的页面跳转,我们这里的Single-App Architecture 有可能是SPA 也有可能是MPA,它们是不同维度的概念需要加以区别。
多应用架构 (Multi-App Architecture)
我们可以把上述应用拆分成多个应用,不仅仅可以按照业务拆分,我们也可以 将同一个业务拆分成小的子领域,例如预定前和预定后可以拆分成不同的应用。
例如上述的应用我们可以按照以下维度拆分

最后页面会拆分成
- 首页应用 (比较特殊,我们先不讨论)
- 机票列表页
- 机票预定页
- 酒店列表页
- 酒店预定页
- 火车列表页
- 火车预定页
我们采用多个单体应用 组成一个 多应用架构,不同的应用负责不同的业务领域,通过相互跳转的方式传递信息从而串联这个系统,这样就不会出现上述不同业务之间的相互影响,不同的业务团队发布各自的系统即可。
但是这里有一个页面是特殊的首页,首页中同时包含了三个页面,那个这个应用应该由谁来维护呢,如果我们仍然采用上述的SPA 架构,同样会面临多个团队开发同一个应用的困境,问题任然没有解决,我们来梳理一下这种模式的优点和
优点
- 发布更加灵活
- 不同的领域相互隔离
- 开发者专注于自己的领域
题外话:强列建议跳转通过TS约定好类型,封装成单独的方法对外暴露,跳转的参数问题是最容易出现问题的之一,而且是最常见的增加沟通成本的环节。
公共依赖如何解决?
但是新的问题也就产生了,虽然是独立的业务,或者同一业务的不同的页面之间,其中难免有一公共的东西,例如:
- 公共的头部
- 公共的底部
- 工程化代码
- UI组件库
- 请求库
- 环境变量(例如语言) ,用户信息
- 和Native 交互的 AppSdk等
多应用+共享模块
为了解决按照领域划分的 公共依赖问题
一个有经验的开发者这时候会想到 模块化 npm/模块联邦等,没错,在实际工作中很多团队就是这样解决的,总结下来有两种方式引入。
构建时引入
我们可以将一些公用的组件,业务逻辑,底层逻辑封装成单独的组件,在使用的时候安装,这样我们就实现了代码的重复利用,让业务逻辑更清晰,专业的人做专业的事 。
渲染时引入
我们可以在引入的时候采用远程的url 引入,或者我们可以采用 模块联邦的方式引入模块,这样做的好处是,公共模块的更新无需让业务方再安装一遍,公共模块的开发者也能更好的控制更新迭代。让然前提是更新是向下兼容的,对于那些破坏性的更新,应该遵循版本号的约定,需要使用方重新安装并测试再去使用往往更加稳妥。
这两种方案各有利弊。
构建时引入:公共库 的版本升级一定要伴随着使用方(业务方)的重新安装、测试、发布。
渲染时引入:在没有破坏更新的情况下,公共库的开发方发布后,使用方无需重新走发布流程,但是在公共库的开发者需要考虑更多的兼容性,需要经过充分的测试后发布
需要注意的是渲染时候引入我们同样可以使用服务端渲染,在服务器端预加载模块让其变成静态模块,渲染时引入可以采用客户端渲染也可以采用服务端渲染,两者并不冲突。
构建时引入 | 渲染时引入 | |
---|---|---|
支持动态更新 | 不支持 | 支持 |
调试成本 | 高/中 | 中/低 |
沟通成本 | 高 | 低 |
职责分离 | 高 | 低 |
服务端渲染 | 支持 | 支持 |
我们拿一个真实案例来看看,对于上述的首页,机票,酒店,火车票的业务方你会采用哪种模式呢
构建时引入还是渲染时引入呢?

上述的架构很好的拆分了业务,也很好的处理了业务公共模块,已经能够满足大部分业务场景,但是
仍然有不足之处 ,甚至可以说是最大的瓶颈。
跨应用上下文断裂(Context Fragmentation)
在多应用架构中,业务被拆分成多个独立的应用。当用户从一个应用跳转到另一个应用时,虽然可以通过 URL 参数 、Storage 等方式传递部分信息,但却缺乏一个真正意义上的 页面级上下文。
所谓"页面级上下文",指的是:
- 用户打开页面时存入变量;
- 页面关闭时变量随之销毁;
- 上下文仅对当前页面有效,不受其他页面影响。
然而,浏览器原生能力并不能满足这一点:
sessionStorage
生命周期依赖于浏览器窗口,而非单个 tab;localStorage
则是全局共享,更不适合做单链路的上下文管理。
典型场景:渠道权益校验
举一个真实的业务需求(我在两家公司都遇到过类似问题):
只有通过特定渠道链接进入的用户,在下单时才会享有某项权益。
在这种场景下,常见的实现方式有两种:
- URL 参数传递
- 必须将渠道 code 等信息从入口页面一路传递到下单页面;
- 链路长、依赖多,极易遗漏。
- Storage 存储
sessionStorage
/localStorage
虽能跨页面共享,但无法限制在单链路内;- 无法在 tab 关闭时销毁,容易污染或错误复用。
为了实现这样的需求,我们动用了 近5个团队改近了近百处跳转,任何一个环节遗漏,都会导致整个业务链路中断,我们团队相对较好是因为我们团队内部使用了微前端,跳转对于我们是可控的。
为什么需要页面级全局上下文
一个真正的 页面级全局上下文,不仅能解决上述链路问题,还能带来额外价值:
- 全局开关
- 比如某个功能需要所有产线统一遵循。
- 用户信息管理
- 用户语言、偏好设置等信息无需重复传递。
- 页面通用元素
- 例如 Header、Footer,避免重复接入和维护。
虽然也有替代方案,但这些方案往往繁琐、冗余,远不如原生的上下文模型简洁高效。
多应用架构的弊端总结
在缺乏页面级全局上下文的前提下,多应用架构存在以下核心弊端:
- 断裂的上下文
- 应用间无法自然共享上下文,导致信息传递冗余且容易出错。
- 用户体验的割裂
- 页面跳转出现白屏、加载延迟;
- 返回操作需要重新刷新页面;
- 与原生 App 的流畅体验差距显著。
- 公共库的维护与升级成本高
- 系统级逻辑(监控、埋点、统计)需要每个应用单独接入;
- 公共依赖(如请求库)分散在各个应用,升级需要逐一改造;
- 关注点没有真正分离,造成了巨大的冗余和协作成本。
案例二:请求库的升级代价
以请求库升级为例:
某次网关改造要求将自建请求库替换为集团共建方案。由于请求库分散在各个应用角落,升级工作量巨大:
- 需要在所有应用中重新安装依赖、修改调用方式、适配新库;
- 通过统计数据显示,仅前端侧就耗费了一名工程师 一年半的全部时间;
这并非个体执行力问题,而是 架构层面缺乏统一治理能力:
- 没有明确规划哪些是公共能力、哪些由业务方负责;
- 即便有规范,长期迭代中也容易走样;
- 团队更替、新方案出现时,容易偏离原有约定。
强制依赖"人来遵守"往往效果有限;
如果有更强的 架构机制 来分离公共与业务逻辑,问题会更容易解决。
答案:基座式微前端
想要解决上述问题:
- 上下文断裂
- 用户体验割裂
- 让业务团队专注于业务;
- 公共 package 可以灵活迭代;
- 架构策略能够强有力地执行;
答案就是 ------ 基座式微前端架构。
微前端的思想通过在运行时整合多个应用,提供统一的上下文与公共依赖治理机制,天然解决了多应用架构的割裂问题。
为什么是基座式微前端?
需要特别说明的是,微前端的概念本身非常广泛:
前端应用可以被拆分为多个子应用,再通过 构建时集成 、服务端拼装 或 浏览器运行时加载 等不同方式组合。无论是哪种形式,都可以被称为"微前端"。例如 Module Federation 、Bit 等方案,本质上都属于微前端的范畴。
但是,这些方案并不一定能解决我们前面提到的关键问题:
- 上下文断裂
- 用户体验割裂
- 公共依赖升级困难
要真正解决这些问题,需要的是 ------ 基座式微前端(Host-based Micro Frontend)。
什么是基座式微前端
基座式微前端,也可以叫做基座-子应用架构:
- 由一个 基座应用(Host App) 负责全局上下文、路由和依赖管理;
- 子应用(Sub Apps)在基座之上运行,并与基座共享上下文(如用户信息、语言、功能开关等);
- 页面跳转无白屏,体验流畅一致;
- 公共库和系统级逻辑集中治理,避免重复接入和难以升级的问题。
基座式微前端的优势
在讨论这些优势之前,需要明确一点:只有在合理且有效地使用基座式微前端架构的前提下,才能真正发挥其优势。举个例子,即便采用了微前端架构,如果各团队仍然各自实现登录逻辑,页面跳转方式与传统 MPA 相同,那么微前端带来的好处几乎无法体现。换句话说,合理使用、并由专门团队维护基座应用,是发挥微前端优势的前提。
1. 职责分离
这里的职责分离更多强调纵向分离,即基础能力开发与业务开发之间的分工,而非不同业务模块之间的分工。
- 每个子应用所需的通用能力,无需业务团队重复实现,可由基座应用统一提供。例如:
- 错误监控
- 用户行为记录
- 权限控制
- 全局 UI 主题管理
- 子应用间通信
- 应用级灰度发布
- 应用出错时的回退处理
这种纵向职责分离可以让业务团队专注于核心业务开发,提高开发效率并降低重复劳动。
2. 版本共存
在开发新功能时,我们通常会进行灰度发布,但很多情况下这只是简单的开关和兼容旧版本,而不是严格意义上的灰度。
- 微前端架构允许同一应用的多个版本共存。基座应用在载入子应用时可以决定使用哪个版本,从而实现真正的灰度发布和版本回退。
- 举例来说,如果某周发布的新版本包含对酒店、机票模块的重大修改,想回退到上一个版本,只需将应用注册表切换回旧版本即可。
- 当然,这里的回退仅指前端代码层面;如果涉及配置变更,也需进行相应回退。如果其他内容也实现了版本化处理,版本回退甚至可以实现自动化,无需人工干预。
3. 解决多环境问题
当不同应用处于不同测试环境时,统一跳转到同一测试环境往往非常麻烦,沟通成本很高。
- 微前端架构下,只需将开发中的应用指向对应环境,其他应用保持稳定版本即可,极大简化了多环境管理。
4. 跨应用用户体验提升
- 及时响应:页面切换可快速完成,同时支持友好过渡动画,避免传统点击跳转的突兀感。
- Keep-Alive:支持页面回退时保持上一页的 DOM 状态,提高用户体验和操作连续性。
5. 跨应用性能优化(不是微前端独有,但是更容易实施)
微前端架构在性能上也有明显优势:
- 请求复用:避免重复请求,提高加载效率。
- 预加载:提前加载子应用资源,缩短等待时间。
- 预请求:根据用户操作预判加载下一步可能需要的资源,提高响应速度。
6. 全局会话管理
基座式微前端架构支持全局会话机制:
- 无论应用如何跳转,只需提供统一的全局会话存储变量,用户状态即可持续存在。
- 例如多模块的修改操作,不再因跳转而丢失用户会话信息。
总结来说,基座式微前端在职责分离、版本管理、多环境支持、用户体验、性能优化和全局会话管理等方面都具有显著优势,但前提是必须合理使用,并由专门团队维护基座应用。
微前端常用的框架和技术栈
客户端集成 --- single-spa
single-spa 是一个 JavaScript 微前端框架,用于将多个单页面应用(SPA)聚合为一个整体应用。通过使用 single-spa 进行前端架构设计,可以带来以下好处:
- 在同一页面中集成多个前端框架,而无需刷新页面(如 React、AngularJS、Angular、Ember 等)。
- 支持每个单页面应用的独立部署。
- 新功能可以使用新框架,而旧的应用无需重写即可与之共存。
single-spa 的基座应用非常轻量,甚至无需使用 React。子应用只需导出符合约定的生命周期方法:bootstrap、mount、unmount。
不过,single-spa 也存在一定的局限性:
- 子应用以 JavaScript 形式存在,只能在客户端加载并渲染。
- 不提供沙箱机制。如果多个子应用共存,需要各自负责清理副作用,否则可能产生冲突。
带沙箱的客户端集成 --- qiankun / wujie / microApp
与 single-spa 类似,这类框架同样通过在客户端组装多个子应用来实现微前端。
不同的是,它们引入了沙箱机制 ,利用浏览器特性(如 Proxy
、iframe
或快照恢复)对每个子应用的全局变量和副作用进行隔离,从而保证安全边界,避免子应用之间的相互影响。
这使得它们更适合横向拆分的复杂页面场景,能够在保证独立性的同时,提供更稳定的运行环境。
Module Federation (MF)
Module Federation 是一种 JavaScript 应用的分治架构模式(类似于后端的微服务)。它允许多个应用(或微前端)之间共享代码和资源,从而支持应用的拆分与按需加载。
需要注意的是,Module Federation 解决的核心问题是**"如何在应用之间加载和共享模块"**。换句话说,采用 Module Federation 并不一定就是微前端架构,而实现微前端也不必依赖 Module Federation
模块联邦和上述的方案完全不冲突,single-spa/qiankun / wujie / microApp 解决的是应用的加载卸载,Module Federation 解决的更多是模块导出与加载,只不过你也可以将应用看做一个模块,利用 Module Federation导出给Host 应用使用,你也可以在使用其他微前端框架的情况下在某个子应用中使用Module Federation导入组件,他们是可以共存的。
接下来我们重点讨论一下 带沙箱的客户端集成 --- qiankun / wujie / microApp,说到微前端大部分人都会想到这几个方案。
qiankun / wujie / microApp 这些微前端框架看上去已经很成熟了,但是在C端使用这些框架的却很少,而且在大部分文章中都会说微前端不是银弹,我想更多的是因为这些框架的缺点有关,而不是微前端这种理念,正是因为这些缺点让很多人在落地微前端的时候望而却步。
客户端集成微前端的缺点
客户端渲染依赖强
- 子应用通常以 JavaScript bundle 的形式存在,只能在浏览器端加载和渲染。
- 对于那些本来支持服务端渲染或者流式渲染的子应用,基座应用也需要等html 加载后解析,最终的呈现仍然是客户端渲染而不是真正的服务端渲染,很多框架所说的支持服务端渲染,准确的来说应该是支持将子应用的服务端渲染在基座应用中以客户端渲染的方式嵌入,而不是真正的在服务端渲染。
全局状态与副作用管理复杂
- 多个子应用共存时,全局变量、事件绑定、定时器等副作用容易互相影响。
- 如果框架不提供沙箱(如原生 single-spa),每个子应用需要自行处理副作用的清理,否则可能导致冲突或内存泄漏。
- 即便是有沙箱仍然有逃离沙箱的方法
额外的性能开销
- 沙箱离不开with 函数,proxy,甚至客户端对js/css的重写,增加了额外的性能开销,
with
打破了静态作用域,使得引擎无法进行优化,函数调用、属性访问和循环性能都会下降。
调试变得复杂
子应用不再是一个完整的系统,在开发时需要启动多个应用,如果在落地时不考虑这些因素,往往会导致开发者的效率变低,我想这是很多开发者讨厌微前端的原因之一。
即便微前端有这么多优点,但是在落地具体框架中任然存在这么多问题,这些缺点丝毫不影响它的优点,我想这并不是微前端这种理念的问题,只能说明目前的框架任然存在很多问题需要解决,甚至很多方面我们需要重新思考。
更好的微前端
参考 Cloudflare Workers 和微前端:为彼此而生 这篇文章,我们可以获得一些启发。实际上,这种方案的核心思想完全可以脱离 Cloudflare Worker 独立存在。结合我们的痛点和实践经验,我们可以构建出一个更加高效和安全的微前端解决方案。
构建时沙箱
现有的微前端沙箱机制大多是在客户端实现的,这带来了额外的运行时开销,并且难以及早发现潜在问题。更理想的方案应该在构建阶段就完成沙箱处理:
- 子应用隔离:在构建时就能确保每个子应用的代码符合规范,同时在开发阶段就能发现试图"逃离沙箱"的写法。
- CSS 沙箱:通过在构建阶段解决样式冲突,可以避免客户端额外的样式处理,从而提升性能。
- 自动卸载逻辑:我们可以在构建时将子应用的卸载代码集成进去,使其在宿主应用中表现得就像一个普通组件,无需宿主额外处理。
在这里不得不提一下 bit.dev。bit.dev 支持将应用的工程化代码(例如脚手架、webpack 配置等)也作为一个组件进行管理。假如修改了工程化组件,所有依赖这些组件的项目都可以触发重新构建。这种思想对于微前端落地非常关键,因为构建本身直接影响代码的最终呈现。如果工程化代码无法高效迭代,微前端的可维护性和稳定性都会受到严重影响。
支持流式渲染(Stream SSR)
为了提升用户体验,我们希望微前端能与服务端渲染(SSR)兼容,甚至支持流式 SSR(Stream SSR)。很多人认为微前端和 SSR 冲突,但实际上并非如此:
- 如果采用构建时沙箱,子应用可以在宿主应用的服务端渲染阶段就开始请求数据。
- 子应用可以在宿主应用渲染 HTML 时同步返回部分内容,实现流式渲染。
- 对用户而言,这种方式与传统 SSR 应用体验无异,同时避免了客户端额外的加载和渲染开销。
换句话说,微前端并不必然牺牲 SSR 或流式渲染性能,通过合理的构建时策略,子应用可以像普通 SSR 组件一样参与服务端渲染,同时保持隔离和独立性。
等到我们拥有这样的微前端架构我们再来回答,微前端是不是银弹这句话也不迟,我相信AI也会加速这类框架的诞生。
构建更高效、灵活的前端开发平台
尽管"更好的微前端"形态尚未完全到来,但通过合理使用微前端,我们依然能够构建一个可插拔、可演进的前端开发平台,带来诸多优势:
-
提升迭代效率
公共库的开发者能够更高效地升级和维护基础库,业务开发者则能专注于领域业务,成为真正的业务专家,减少对公共库的额外关注。
-
增强复用能力
- 代码复用:通用逻辑沉淀并复用,避免重复开发。
- 运行时复用:通过微前端,许多原本需要单独请求的业务操作只需一次请求,减少性能开销。
-
优化用户体验
微前端实现跨应用跳转无需刷新页面,且能保持前一个应用的状态,带来更接近原生应用的流畅体验。
-
减少架构限制
微前端打破了传统架构的上下文隔离限制,原本因为架构限制而无法实现的需求,如今能通过微前端灵活地解决,开发者无需依赖高人力成本的替代方案。
-
规范化落地
基座应用不仅承担渲染任务,还能负责监控和规范实施,帮助发现和阻止不规范的 API 调用,如未加业务前缀的存储操作、不合规的跳转链接等。
-
强化混合应用能力
在 Hybrid 场景 下,将负责与客户端交互的 App SDK 内置到基座。基座对 WebApp 提供稳定的桥接 API,SDK 升级只需更新基座,业务层几乎零感知,极大地减少了升级成本并提高了版本控制的灵活性。
大前端通常服务一个大的产品,而这个大的产品既需要分而治之,又需要代码公用,还需要高效迭代。微前端的架构正好给我们提供了这样一个可能。
我在做百变AI助手时,就使用了上述基座微前端的架构,能够做到AI生成的代码在各个中平台运行,因为适配宿主环境的能力和子应用调用的能力都是通过基座应用实现的。
有了基座应用的桥接,我无须担心后续宿主环境的调整需要子应用重新生成,即便需要重新生成,我也可以通过基座应用监控那些应用需要重新生成。
如果这篇文章对你有帮助,记得关注,后续我还会持续分享更多做项目的真实体验。