❝本篇文章全文约 1.5 万字,目的是系统化地介绍微前端及其核心技术,并介绍了什么是微前端以及为什么我们需要它。我们还讨论了在众多微前端框架中如何选择适合自己系统的框架,并分享了一些业界使用微前端的实践案例。最后,我们提到了微前端目前存在的一些局限性和争议点。总之,希望这篇文章能为大家提供有用的信息。
❞
1 引言
1.1 前端开发的演变历程
「初期的静态网页阶段」
在早期的前端开发中,网页主要由 HTML (HyperText Markup Language) 构建,每个页面都是一个静态的 HTML 文件。这些页面通常包含基本的文本、链接和图片,用户与之交互的方式非常有限。这时期的网站主要是信息展示,交互性较弱。
「动态网页和 JavaScript 的出现」
随着 JavaScript 的出现,前端开发开始进入新的阶段。JavaScript 为浏览器提供了执行代码的能力,使得网页变得更加动态和交互性强。例如,用户可以通过点击按钮来改变网页内容,或者通过表单输入数据。这一阶段的前端开发开始了前后端交互的初步实现。
「AJAX 和 Web 2.0 阶段」
Web 2.0 标志着网页开发的又一次重大变革。AJAX(Asynchronous JavaScript and XML)技术的出现使得网页可以在不刷新整个页面的情况下从服务器获取数据。这意味着网页可以实时更新,用户体验大大提升。此阶段的网站开始偏向于服务和应用,而非单纯的信息展示。
「单页应用(SPA)和现代前端框架」
随着 AngularJS、React、Vue 等现代前端框架的出现,前端开发进入了单页应用(SPA)的时代。在这个阶段,整个 Web 应用只需要加载一次,之后所有的操作都在客户端进行,只有当需要从服务器获取或更新数据时,才会发出请求。这大大提升了用户体验,使得 Web 应用的表现更接近桌面应用。
「微前端的崭露头角」
微前端是近年来前端开发的新趋势,它是微服务概念在前端的应用。在微前端的架构下,一个大型的前端应用可以被拆分成多个小的、独立运行的应用,这些小应用可以独立开发、独立部署、独立运行。微前端的出现使得大型前端项目的开发和管理变得更加灵活和高效。
1.2 微前端:探索新的前端架构模式
微前端是一种新兴的前端架构模式,其核心理念在于将一个庞大且单一的前端应用拆解为一系列小型、独立的微应用。这些微应用各自作为一个独立的前端应用,拥有自己的开发和技术栈,可以由不同的团队独立开发、部署和维护。这种拆分带来了三大特性:应用自治,单一职责以及技术栈无关性。微前端的设计目标在于提高前端应用的可维护性和可扩展性,以便让开发团队能更加敏捷地开发和部署应用程序。
1.2.1 基座模式与自组织模式
微前端的实现可以通过两种主要模式进行,即基座模式和自组织模式。
基座模式是一种中心化的管理模式,由一个主应用作为所有微应用的入口,负责统一管理和协调。虽然这种模式在设计上相对简单,但其通用性较低。因为在基座模式下,子应用必须遵循基座应用的规范和约定,以便与基座应用进行集成。这种强依赖性在一定程度上限制了子应用的灵活性和独立性。
相反,自组织模式则是一种去中心化的模式,系统内的各个微应用都各自拥有一个小型的基座管理功能,也就是说,每个应用都可以作为基座。这种模式下,应用之间平等且不存在互相依赖,虽然设计起来较为复杂,实现起来也不太方便,但是其通用性较高。
「优点」 | 「缺点」 | |
---|---|---|
「基座模式」 | 设计难度较小,中心化管理带来的协调性和一致性较强 | 通用性较低,子应用必须遵循基座应用的规范和约定,限制了子应用的灵活性和独立性 |
「自组织模式」 | 应用之间平等,不存在互相依赖,具有较高的通用性和灵活性 | 设计和实现难度较大,需要编写和维护较多的重复代码 |
1.2.2 实现微前端的方法
微前端的具体实现方法有很多种,例如可以通过 iframe、JavaScript 模块、Web Components 等方式来实现。这些实现方式都能帮助开发团队将应用程序分解为更小、更易于维护的部分,并且每个部分都可以独立地进行开发、测试和部署。这样,不同的团队或开发者可以专注于自己负责的部分,并独立地进行开发和部署,从而提高整个团队的协作效率和开发速度。
1.2.3 微前端的应用与未来
微前端已经成为现代前端开发中的一个重要发展方向,它吸引了越来越多的企业和开发者的关注。微前端不仅有助于团队更敏捷地开发和部署应用,同时也有利于提高应用程序的可维护性和可扩展性。随着前端技术的不断发展和应用场景的变化,微前端将持续发挥其重要作用,成为前端开发中不可或缺的一部分。
2. 微前端的核心理念
微前端架构的设计原则是将大型前端应用分解成一组较小的、独立运行的微应用。这些微应用可以独立开发、部署,并且有自己的技术栈,但最终它们将被整合到一起,为用户提供一致的体验。下面是微前端的一些核心理念:
「技术栈无关性」:在微前端架构中,每个微应用都可以自由地选择适合自己需求的技术栈,如 React、Vue 或 Angular。这为开发团队提供了更大的灵活性,可以根据应用的特性和团队的技术熟悉度选择最合适的技术。
「自治性与独立部署」:每个微应用都可以独立进行开发、测试和部署,这大大加快了迭代速度,也使得团队能够根据自己的进度和需求来规划工作。例如,一个负责用户管理的微应用可以在不影响其他微应用的情况下,独立更新和优化。
「命名空间隔离」:为了避免全局变量的冲突和污染,每个微应用在运行时都应该保持在自己的命名空间内。这意味着一个微应用中的全局变量或函数不会影响到其他微应用。
「明确的通信机制」:微应用之间的通信应该通过明确定义的接口进行,避免微应用直接访问其他微应用的内部状态或方法。这种通信机制可以是事件发布/订阅,也可以是通过全局状态管理库。
「整合统一」:尽管每个微应用都是独立开发和部署的,但在用户使用时,它们应该被整合在一起,提供一个统一和流畅的用户体验。这需要对微应用间的交互和切换进行精心设计和管理。
「高内聚低耦合」:每个微应用都应该聚焦于完成自己的特定功能或业务领域,这种高内聚性使得代码更易于理解和维护。同时,微应用间应该尽量保持低耦合,避免因一个微应用的改变而导致其他应用需要修改。这样,每个微应用都可以成为一个可独立更换和升级的部分。
这些原则共同构成了微前端的核心理念,旨在支持快速的迭代,易于维护,同时保证用户体验的一致性和流畅性。微前端实际上是一种将微服务的理念扩展到前端开发的方法,它解决了单体应用规模扩大后难以维护和升级的问题,同时避免了微服务可能带来的复杂性。
3 选择合适的微前端框架
了解当前已经有的微前端框架,可以帮助开发人员选择适合自己项目的技术方案。在详细讨论微前端的关键技术之前,我们将介绍一些比较常见的微前端框架,比如: Single-SPA、qiankun、无界、microapp 等等。
框架名称 | 所属方 | 「描述」 | 「沙箱机制」 |
---|---|---|---|
「路由分发式」 | 原始 | 将不同的路由分发到不同的微应用的策略,使其看起来是一个整体。 | 无 |
「iframe」 | 原生 | 使用 iframe 进行微应用的隔离和集成。 | 有天然的沙箱环境,可以隔离全局对象、样式等 |
「single-spa」 | 社区 | 一个用于将多个独立开发的应用整合到一个页面的 JavaScript 微前端框架。 | 无 |
「qiankun」 | 蚂蚁 | 基于 Single-SPA,提供简单易用的 API、多种通信和协调机制。 | 有基于 proxy 实现的沙箱机制 |
「icestark」 | 阿里 | icestark 是面向大型系统的微前端解决方案,适用于后台系统分散、体验差异大、开发成本高、需要接入二方/三方模块的场景。 | 有基于 proxy 实现的沙箱机制 |
「micro-app」 | 京东 | 基于类 WebComponent 进行渲染,从组件化的思维实现微前端。 | 有基于 proxy 实现的沙箱机制 |
「wujie」 | 腾讯 | 基于组件化的思维实现微前端,旨在降低上手难度、提升工作效率。 | 基于 iframe 的沙箱机制 |
3.1 路由分发式微前端
「原理」
路由分发式微前端通过路由将不同的业务分发到各个独立的前端应用上。这一原理通常可以通过 HTTP 服务器的反向代理来实现,或者可以借助应用框架自带的路由系统来解决。例如,下面的配置就展示了如何用 Nginx 服务器的反向代理来将请求分发到不同的微应用:
http {
server {
listen 80;
server_name xxx;
location /api/ {
proxy_pass http://192.168.0.3:8000/api;
}
location /web/admin {
proxy_pass http://192.168.0.1/pc/app1;
}
location /web/notifications {
proxy_pass http://192.168.0.2/pc/app1;
}
location / {
proxy_pass /;
}
}
}
「特点」
主要依赖于路由来区分和加载不同的微应用。
当用户导航到某个路由时,主应用将决定加载哪个子应用。
每个子应用可以拥有自己独立的技术栈、开发团队和部署策略。
「优点」
「团队独立性」:每个团队可以独立地开发、测试和部署自己的微应用。
「技术栈灵活性」:不同的微应用可以使用不同的技术栈。
「风险隔离」:一个微应用的问题不太可能影响到其他微应用。
「缺点」
「复杂性增加」:需要管理子应用之间的通信、样式隔离和 JS 隔离等问题;
「状态共享复杂」:管理多个子应用之间的共享状态可能会变得复杂;
「用户体验」:由于每次切换应用时,浏览器都需要重新加载页面,可能会影响用户体验;
「微应用并存问题」:多个微应用无法在同一页面并存,这可能限制了一些交互设计;
「局限性」:由于依赖于路由进行应用分发,这种方法在一些使用场景上可能会相对受限。
3.2 原生 iframe
「原理」
使用 iframe 标签来嵌入和隔离不同的微应用,实现微前端的设计原则。每个子应用都在其自己的 iframe 容器中运行,与其他子应用完全隔离。
「优点」
「隔离性强」:iframe 提供了天然的沙箱隔离,使得 CSS、JavaScript、DOM 之间不会相互影响。
「实现简易」:只需通过加载相应的 iframe 页面,就能独立运行子应用。
「兼容性好」:基于标准 HTML 特性实现的 iframe,兼容各种浏览器。
「扩展性强」:可以同时加载和显示多个子应用。
「缺点」
「URL 不同步」:iframe 的 URL 与主应用的 URL 不同步,导致浏览器刷新时 iframe 的 URL 状态可能丢失,后退或前进按钮可能无法正常使用。
「UI 不同步」:由于 DOM 结构不共享,可能会导致用户界面(UI)的不一致。
「全局上下文隔离」:全局上下文完全隔离,内存变量不共享。这意味着 iframe 内外系统的通信、数据同步等可能会变得复杂。例如,主应用的 cookie 需要透传到根域名都不同的子应用中才能实现免登效果。
「性能问题」:由于每次进入子应用都是一次浏览器上下文重建、资源重新加载的过程,可能导致应用性能降低,影响用户体验。
3.3 社区 single-spa
single-spa[1]是最初引入的微前端 JavaScript 框架之一,以其对多种前端技术栈的兼容性而闻名。
「原理」
single-spa 的核心是一种运行时协议,这个协议定义了主应用与子应用之间的交互规则。具体来说,这个协议规定了主应用如何配置不同的子应用,以及子应用在不同情况下应执行的生命周期钩子。这使得主应用能精确地感知和控制每个子应用的挂载和卸载时机。
「优点」
「多框架支持」:single-spa 支持多种前端框架,包括 React、Angular、Vue 等。这意味着你可以在同一应用中混合使用不同的前端技术栈,无需重写现有代码。
「独立部署和开发」:每个微前端应用都可以独立部署和开发,使得团队可以独立地迭代和发布他们的部分,而不会影响整个应用。
「增量升级」:在面对复杂场景时,全量技术栈升级或重构通常难度较大。微前端提供了一种实施渐进式重构的有效策略。
「友好的 jQuery 支持」:single-spa 对 jQuery 的支持友好,方便了传统网页转换为微前端的过程。
「缺点」
「复杂性」:single-spa 作为一个框架,需要额外的学习和配置,可能增加项目的复杂性。
「性能开销」:按需加载和运行多个微前端应用可能会带来一些性能开销,尤其是在初始加载时,需要仔细优化和配置以保证良好的性能表现。
「无沙箱机制」:对于 CSS 和 JavaScript 的隔离需要手动实现,例如通过 Shadow DOM 或者 Proxy。
「无内置通信机制」:微应用间的通信需要自己实现,可能需要借助全局状态、自定义事件或其他第三方库。
「无内置应用生命周期管理」:single-spa 并未提供应用的加载和卸载机制,需要开发者自行实现。
「无全局状态管理」:single-spa 本身并不提供全局状态管理,需要与其他状态管理库配合使用。
单独探讨某一微前端框架的优点,很难脱离 single-spa 的影响。因此 single-spa 存在的优点下面的框架都基本存在,这里便不一一重复。
3.4 蚂蚁 qiankun
qiankun[2]是蚂蚁金服前端团队基于 single-spa 开发的微前端框架,它提供了更完善、更先进的功能和解决方案,以帮助开发者更轻松地构建微前端应用。
「原理」
qiankun 是基于 single-spa 实现的微前端框架,但有所不同,它采用 html Entry 的方式加载子应用。
qiankun 提供了两种 CSS 隔离模式:严格模式 strictStyleIsolation(基于 Shadow DOM)和实验性模式 experimentalStyleIsolation(基于 scoped CSS 的思想,通过运行时修改 CSS 选择器来实现子应用间的样式隔离)。
qiankun 设计了三种沙箱机制:单例沙箱(包括 SnapshotSandbox 基于 diff 和 legacySandBox 基于 proxy)以及多例沙箱(proxySandbox 基于 proxy)。
qiankun 提供了简单的 props 和 actions 全局状态管理和通信机制。
「优点」
「自动分析和加载」:qiankun 能自动分析 html 以获取 js 和 css,不需要开发者手动指定如何加载。
「沙箱机制」:基于快照和 Proxy 的思路实现了 JS 隔离,基于 Shadow Dom 和 scoped css 的思路实现了 CSS 隔离。
「提供通信机制」:qiankun 提供了 acitons 全局状态管理的机制和 props,用于应用间的通信。
「自动应用加载和卸载」:qiankun 能监听路由变化,实现当前路由对应的子应用的自动加载和卸载。
「缺点」
「路由限制」:qiankun 基于路由匹配,无法同时激活多个子应用。
「适配成本高」:从生命周期、静态资源路径、路由、webpack 配置等方面都需要做一系列的适配工作,可能导致适配成本较高。
「CSS 沙箱隔离不完全」:严格模式基于 Shadow DOM,虽然形成了天然的隔离,但第三方组件的弹窗默认挂在到 body 下面,这样弹窗中的自定义样式会失效,需要手动设置挂载节点。实验性模式类似于 scoped css 模式来隔离,对于样式名冲突时,也可能出现问题。
「不支持 ESM 脚本」:qiankun 官方不支持如 vite 等 ESM 脚本的运行。
「可能导致数据通信沉重」:由于数据都是由基座派发下来的,可能在大量数据交互的情况下导致通信沉重。
3.5 阿里 icestark
icestark[3]是飞冰团队为大型系统提供的微前端解决方案。为了方便 icejs 应用快速接入微前端解决方案,飞冰团队还专门提供了一个名为 build-plugin-icestark 的独立插件。
「原理」
icestark 支持所有主流浏览器,可以直接在飞冰项目中使用。
icestark 与飞冰的迭代和维护同步,保证了其长期的稳定性和可靠性。
icestark 的通讯方式是基于 Event 和 emit,所有的事件都放在 window.store 下进行管理。
CSS 隔离采用的是 Css Modules,而 Shadow DOM 目前仍在试验阶段。
对于 JS 污染问题,icestark 采用了基于 Proxy 的运行时沙箱。
「优点」
icestark 可以帮助 icejs 应用快速接入微前端解决方案,提升开发效率。
icestark 支持微应用使用 Vite 模式,扩展了开发选型的可能性。
「缺点」
icestark 不支持多个微应用同时存在,这对于某些需要并行处理多任务的场景可能会造成限制。
CSS 隔离不彻底,可能存在样式污染的风险。
JS 污染问题也没有完全解决,可能影响到应用的稳定性和安全性。
通过 window.store 进行通信可能存在安全风险,数据可能被劫持和篡改,需要额外的安全防护措施。
3.6 京东 micro-app
micro-app[4]是京东零售推出的一款微前端框架。该框架以类 WebComponent 的方式进行渲染,采用了组件化的思维来实现微前端,目的在于降低上手难度并提升开发效率。
「原理」
micro-app 借鉴了 WebComponent 的思想,它通过 CustomElement 结合自定义的 Shadow DOM,将微前端封装成一个类 WebComponent 组件,从而实现微前端的组件化渲染。
「优点」
「简单易用」:micro-app 的使用方式类似于 iframe,上手简单。
「侵入性低」:对原有代码的影响微乎其微。
「功能丰富」:micro-app 提供了 js 沙箱、样式隔离、元素隔离、预加载、数据通信、静态资源补全等丰富的功能。隔离机制类似于 qiankun,例如,样式隔离基于 scoped css,js 沙箱机制基于 Proxy。
「零依赖」:micro-app 不依赖于任何第三方库。
「缺点」
「静态资源补全问题」:静态资源补全是基于父应用的,而非子应用。这需要开发者自己手动解决。
「接入成本」:虽然接入成本相比 qiankun 有所降低,但路由依旧存在依赖。
「路由状态保持问题」:在多应用激活后,无法保持各子应用的路由状态,刷新后全部丢失。
「隔离问题」:css 沙箱使用 scoped css 思想,但无法绝对隔离,例如可能存在样式名冲突。js 沙箱在做全局变量查找缓存时,虽然性能有所优化,但仍存在隔离问题。
「Vite 运行支持」:虽然支持 vite 运行,但必须使用 plugin 改造子应用,且 js 代码无法进行沙箱隔离。
「浏览器兼容性」:对于不支持 webcomponent 的浏览器,micro-app 没有进行降级处理。
3.7 腾讯 wujie
wujie[5]是腾讯推出的一款微前端解决方案,主要基于组件化的思维实现微前端,旨在降低上手难度并提升工作效率。
「原理」
Wujie 框架的核心技术是基于 WebComponent 容器与 iframe 沙箱来实现微前端。
「优点」
「简洁易用」:Wujie 采用组件式的使用方式,接入简单,与 micro-app 有类似的简洁性。
「彻底隔离」:虽然 CSS 隔离是基于 Shadow DOM,但 iframe 通过 proxy 的方法将 DOM 劫持到 Shadow DOM 上,提供了彻底的隔离。
「轻量级」:Wujie 借助 iframe 和 webcomponent 来实现沙箱,有效减小了代码量,使得其体积较为轻量。
「支持 Vite」:Wujie 支持使用 Vite 进行快速开发。
「缺点」
「内存开销」:承载 js 的 iframe 是隐藏在主应用的 body 下面的,相当于是常驻内存,这可能会带来额外的内存开销。
「技术栈限制」:Vue、React、Angular 等都需要安装它自己的版本,这可能对原有的项目产生影响。
选择微前端方案的一些建议
在选择微前端框架时,可以参考上述的分析,并结合框架的最新更新时间、社区活跃度等因素,以找到最适合自己需求的前端框架。以下是一些常见微前端框架的对比:
「框架」 | 「git stars」 | 「代码最近更新时间」 | 「社区活跃度」 | 「接入成本」 |
---|---|---|---|---|
「路由分发式」 | 无 git | 无 | 无 | 高 |
「iframe」 | 无 git | 无 | 无 | 高 |
「single-spa」 | 12.6k[6] | 2023-08-09 | 较高 | 高 |
「qiankun」 | 14.8k[7] | 2023-09-07 | 高 | 较低 |
「icestark」 | 1.9k[8] | 2023-08-23 | 较低 | 较低 |
「micro-app」 | 4.5k[9] | 2023-09-08 | 较高 | 低 |
「wujie」 | 3.2k[10] | 2023-08-24 | 较高 | 低 |
上图更新时间截止:2023-09-10
基于以上对比,如果是「老项目」需要引入微前端架构,qiankun 可能是一个比较适合的选择。对于「初次尝试」微前端的项目,wujie 和 micro-app 也是值得考虑的选项。
虽然 single-spa 在接入成本上可能高于其他框架,但一旦掌握了 single-spa,其它框架的使用将变得相对顺利,因为 single-spa 在微前端框架中起到了概念引领者的角色。
需要注意的是,虽然 iframe 存在一些问题,但在某些特定的情况下,它仍然是一个有效的工具。例如,当需要快速将其他应用程序,如广告页,报表等仅简单嵌入到系统中时,使用 iframe 可能是一个合适的选择。
4 微前端集成要点
实施微前端的过程并非易事,它需要深入了解和娴熟掌握一系列关键技术,包括路由管理、全局状态管理及通信、性能优化,以及错误隔离与处理等。下面,我们将详细探讨这些关键技术:
4.1 微前端中的路由菜单管理
微前端架构允许微应用独立运行,每个微应用都具有自己的路由菜单导航逻辑。这样就带来了一个问题,如何将所有微应用的菜单整合到主应用中。为解决这一挑战,我们可以采取以下三种策略:
4.1.1 中心化路由管理
在中心化路由管理模式下,所有微应用的路由被集中管理,由主应用维护全局路由表和菜单,并将微应用的路由信息融入到全局路由表中。在这种模式下,微应用需要通过主应用获取自己的路由信息,并依据该信息进行路由控制和导航。
「优势:」
「统一性」:由于所有的路由信息都在主应用中进行集中管理,避免了不同微应用的路由冲突,使得路由的维护和管理更为方便。
「可控性」:主应用能够集中管理所有路由和权限,提供了更好的控制能力。
「劣势:」
「耦合性」:微应用需要依赖主应用,这限制了微应用的灵活性。
「集中化」:随着微应用数量的增加,主应用的逻辑负担可能过重,可能造成单点故障。
以 qiankun 框架为例,中心化路由管理的实现关键步骤如下:
在主应用中定义全局路由表和菜单,将各个微应用的路由信息整合到全局路由表中。
主应用获取全部路由表,在注册子应用时,通过 props 传递给子应用。
// 在主应用程序中定义获取路由信息的接口
function getRoutes() {
// 返回全局路由表
return globalRoutes;
}
// 在主应用程序中注册子应用
qiankun.registerMicroApps([
{
name: 'my-app',
entry: '//localhost:3001',
container: '#my-app',
activeRule: '/my-app',
props: {
getRoutes, // 将获取路由信息的接口传递给子应用
},
},
// ...
]);
在微应用中获取主应用传递的路由信息,并使用 qiankun 框架提供的生命周期函数 "mount" 来实现路由信息的获取。
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export const mount: (props: any) => void = async props => {
await render();
setGlobalData(props.data);
setLoadGlobalFinish(true);
};
4.1.2 动态路由管理
在动态路由管理模式下,主应用和微应用各自维护自己的路由菜单逻辑及控制。微应用向主应用暴露自己的路由菜单数据,主应用在运行时,动态获取微应用的数据来生成自己的路由菜单。
「优势:」
「解耦」:微应用各自维护自己的路由逻辑,实现了微应用和主应用的完全解耦。
「灵活性」:每个应用都可以根据自身的需要定制自己的路由管理策略。
「劣势:」
「难以维护」:由于每个应用都有自己的路由菜单逻辑,这可能导致维护的复杂性提高。
在 qiankun 框架中,动态路由管理模式可以通过以下步骤实现:
微应用暴露出路由菜单数据:微应用需要在自己的入口文件中暴露出自己的路由菜单数据,使用 setGlobalState 方法将数据保存到 qiankun 的全局状态中,供主应用使用。
import { setGlobalState } from 'qiankun';
const routes = [
{
path: '/home',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
];
setGlobalState({
routes,
});
主应用获取微应用的路由菜单数据:主应用可以通过 useEffect 钩子函数和 getGlobalState 方法来获取微应用的路由菜单数据,并保存到主应用的状态中。
import { useEffect } from 'react';
import { getGlobalState } from 'qiankun';
const App = () => {
const [routes, setRoutes] = useState([]);
useEffect(() => {
const routes = getGlobalState('routes') || [];
setRoutes(routes);
}, []);
return (
<div>
<ul>
{routes.map((route) => (
<li key={route.path}>
<Link to={route.path}>{route.name}</Link>
</li>
))}
</ul>
<hr />
<Router>
{routes.map((route) => (
<Route key={route.path} path={route.path} component={route.component} />
))}
</Router>
</div>
);
};
4.1.3 混合式路由管理
混合式路由管理则是中心化路由管理和动态路由管理两者的结合。主应用维护全局的路由表和菜单,微应用则维护自身的路由表和菜单。微应用的路由表和菜单信息会被主应用动态获取和整合。
「优势:」
「灵活性」:微应用可以根据自身需要定制自己的路由管理策略。
「统一性」:主应用能够集中管理所有路由和权限,提供了更好的控制能力。
「劣势:」
「复杂性」:需要在主应用和微应用之间维护路由信息的同步,可能增加系统的复杂性。
混合式路由管理的实现主要是结合了中心化路由管理和动态路由管理的实现方式,主应用需要动态地获取微应用的路由信息,并将这些信息整合到全局路由表中。
4.2 微前端的全局状态管理及通信
微前端作为一种新兴的设计理念,它将大型单体前端应用程序分解成多个独立运行的小型应用。在这种架构下,全局状态管理和通信是一个重要的话题。实现这个目标的方式有很多,以下是几种常见的策略:
「框架自身的状态管理机制」: 比如 qiankun 框架,它提供了官方的 actions 和无界的去中心化通信方案 eventBus 等工具来实现状态管理和通信。
「Redux 或 Mobx」: 对于使用 React 的微前端应用,Redux 或 Mobx 是实现全局状态管理的常见选择。在这种情况下,需要在微前端应用之间共享和同步 Redux store。
「Vuex」: 对于使用 Vue 的微前端应用,Vuex 是一个可行的全局状态管理方案。
在大多数基础场景下,微前端框架本身提供的通信方法已经能够满足需求。然而,在面临一些特殊的业务需求时,可能需要自定义通信解决方案。
接下来,我们将以 qiankun 框架提供的 actions 为例,详细解析如何实现微前端的通信。
首先,我们在主应用中注册 MicroAppStateActions 实例,并导出:
// micro-app-main/src/shared/actions.ts
import { initGlobalState, MicroAppStateActions } from "qiankun";
const initialState = {};
const actions: MicroAppStateActions = initGlobalState(initialState);
export default actions;
然后,在注册子应用的列表中,将 actions 以 props 的形式传递给子应用:
const apps = [
{
name: 'App1',
entry: "http://localhost:8000",
container: "#container",
activeRule: '/vue',
props: { actions} //向子应用传递创建的全局状态
}
]
接下来,当主应用的某个组件(如 ComponentA)的数据发生变化时,我们可以通过调用 actions 的 setGlobalState 方法来通知子应用:
<template>
<div>
<el-button @click="handleNotification">通知子应用1</el-button>
</div>
</template>
<script>
import actions from "@/shared/actions";
export default {
name: 'ComponentA',
methods: {
handleNotification() {
actions.setGlobalState({message:'发送消息'})
}
}
}
</script>
在子应用中接收消息,接受前需要先配置子应用的全局状态 Actions,并在 main.js 中注入父应用传过来的 acitons 实例:
function initMethod(state: unknown) {
console.warn("state----", state);
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: initMethod,
setGlobalState: initMethod,
};
// 设置 actions
setActions(actions: unknown) {
this.actions = actions;
}
// 映射
onGlobalStateChange(...args: any) {
return this.actions.onGlobalStateChange({...args});
}
// 映射
setGlobalState(...args: unknown[]) {
return this.actions.setGlobalState({...args});
}
}
const actions = new Actions();
export default actions;
在 main.js 的 mount 生命周期接收父应用传递的 actions,并注入 actions 实例
export async function mount(props) {
if (props) {
// 注入 actions 实例
actions.setActions(props)
}
render(props);
}
最后,我们在子应用的 pageA.js 中监听全局状态的变化:
<script>
import actions from "./actions";
export default {
name: 'ComponentA',
mounted(){
actions.onGlobalStateChange((state, prevState)=>{
const { message } = state
console.info('接收消息:',message)
})
}
}
</script>
至此,我们已经完成了使用 qiankun 进行微前端通信的全部步骤。这种方法的优点在于,它提供了一种跨应用的通信机制,实现了状态的共享与同步。然而,它也有一些缺点,例如在复杂的业务场景下可能需要定制化解决方案,以及可能存在的性能问题等。
总的来说,选择合适的微前端通信方案,需要根据具体的业务场景和技术栈来考量。既要考虑易用性和可维护性,也要兼顾性能和稳定性。
4.3 微前端的性能优化
在微前端架构中,性能优化是一个不能忽视的重要环节。特别是在同时运行多个微应用程序的情况下,需要从多个角度对性能问题进行深入分析。以下是一些常见的性能瓶颈以及相应的优化策略:
4.3.1 首屏加载速度
在微前端架构中,多个独立的微应用程序可能会对首屏加载速度产生负面影响。
优化策略:
「代码拆分」:将应用的代码拆分成多个小的代码块,然后按需加载。这样可以降低首屏需要加载的代码量。例如,在 React 中可以利用 React.lazy 和 Suspense 进行代码拆分,而在 Vue 中,可以使用异步组件对代码进行拆分。
「预加载」:在浏览器空闲时提前加载下一屏所需的资源,从而提高下一屏的加载速度。可以利用标签或 Webpack 的预取功能来实现。
「资源压缩和优化」:使用 Webpack 等构建工具对代码进行压缩和优化,包括 JS、CSS 和图片等资源。此外,也可以使用 CDN 服务来加速资源的加载速度。
4.3.2 微应用切换性能
由于微前端架构涉及多个独立的微应用,因此应用间的切换可能对性能产生影响。
优化策略:
「生命周期管理」:当应用不再需要时,应及时销毁并回收资源。例如,可以在应用卸载时销毁相关实例,并回收事件监听器、定时器等资源。
「缓存」:对已加载的微前端应用进行缓存,以便下次快速加载。例如,可以在应用加载后将应用实例保存到内存中,下次需要时直接从内存中获取。
4.3.3 全局状态管理
微前端架构可能会使得全局状态的管理变得复杂,从而影响性能。
优化策略:
「避免过度的全局状态管理」:全局状态的管理应该保持简洁,避免无必要的全局状态。过多的全局状态可能会使状态同步变得复杂,同时也可能影响性能。
「优化全局状态的同步」:全局状态的同步应该尽可能高效,避免无必要的同步操作。例如,可以采用观察者模式或发布-订阅模式来进行状态同步。
以上只是一些可能的性能问题和优化策略。具体的优化策略可能需要根据项目的具体需求和环境进行调整。在进行性能优化时,应当牢记一条原则:不要过早优化,所有的优化活动都应该基于实际的性能问题和量化的性能指标进行。
5 微前端的适用场景与案例分析
微前端架构是一种强大的渐进式技术,它在多个不同的应用场景中都可以发挥重要作用。以下是一些微前端的典型应用场景:
-
「系统合并兼容」:当需要集成正在运行的老系统并且不希望重新开发时,可以采用微前端方案将老系统加载到新系统中。这种方式可以使得新旧系统的交互更为流畅,同时避免了开发资源的浪费。
「巨型应用拆分」:对于庞大且复杂的应用,微前端提供了一种有效的拆分策略。通过将大应用拆分为多个微应用,可以将整个团队分解为多个小团队,每个小团队专注于一到两个微应用的开发和维护,这样可以大大提高开发效率和工作质量。
「渐进式技术栈升级」:如果希望尝试新技术,但又不想立即在整个系统中引入,可以选择将一个子应用使用新的技术栈进行开发,观察运行效果。这种方式既保证了技术的更新,又避免了新技术可能带来的风险。
微前端架构的强大之处在于其兼容性和灵活性,只有当你需要使用它时,才需要去了解和采用它。因此,面对问题时,最合适的解决方案就是最好的解决方案。
以下是一些微前端架构的实际应用案例:
「淘系内部的平台化建设:」
淘系内部基于 icestark 进行微前端搭建,将整体系统按功能维度拆分为大约 20 个独立应用,这些应用统一接入到一个平台中。这种方式不仅提升了平台的可维护性,而且使得各应用能够更好地协同工作。目前在阿里集团内部,icestark 已经在 20+平台级的应用中得到验证,覆盖淘宝、菜鸟、飞猪、业务平台等。【链接[11]】
「飞猪一体化运营平台:」
该平台是淘宝旗下的一个基于 qiankun 微前端架构构建的大型应用,包含了很多前端应用,例如订单管理、数据分析、商品管理等。通过使用微前端架构,飞猪一体化运营平台将原来的单体应用程序拆分成多个独立的微应用程序,每个微应用程序都可以独立开发、测试和部署。微前端框架包括 qiankun 等,实现了应用拆分和整合、应用隔离和容错、动态加载和缓存、跨团队协作等优势。每个微应用程序都是独立的,彼此之间没有任何耦合关系。当一个微应用程序发生故障时,不会影响到整个系统的稳定性。此外,使用动态加载和缓存技术可以提高应用的加载速度和用户的使用体验。【链接[12]】
「OneX:」
蚂蚁内部基于 qiankun 微前端框架提出了 OneX 解决方案,可以将大型的系统解耦成可以独立开发的并行的小型系统,并在运行时集成起来。OneX 已经成功应用于 70+ 线上应用接入,最复杂一个控制台同时集成了 15 个应用,有 4+ 不同技术栈。此外,他们还实现了开发到发布上线全链路的自动化支持,一云入驻多云运行。通过 qiankun 微前端框架的支持,蚂蚁内部成功地实现了应用的解耦、可维护性和可扩展性的提升,同时也提高了应用的稳定性和用户的使用体验。【链接[13]】
「美团外卖:」
采用基于 React 的中心路由基座式微前端方案,对业务进行改造和拆分,使得子工程能够按照业务线进行划分,独立维护,同时保证子工程大小可控。微前端拆分方案使得业务不仅在纵向上保有复用能力,更重要的是拥有了横向扩展能力。目前已经有很多个子业务线采用了微前端模式上线,还有多个正在开发的微前端子工程,剩余的子业务线也可以无痛迁移出来成为子工程。【链接[14]】
以上公司通过使用微前端架构来提高应用程序的可维护性、可扩展性和开发效率,并且均取得了不错的成果和效益。
6 微前端的局限性与挑战及争议
微前端的设计理念为解决大规模应用的管理问题提供了新的解决方案。然而,任何架构模型都无法适应所有的使用场景。微前端架构也不例外,它的应用也存在一些局限性和挑战,同时,也引发了一些争议。
6.1 局限性和挑战
6.1.1 性能问题
微前端架构下,每个微应用都可能有自己的依赖和运行环境,这可能导致一些性能开销,如额外的网络请求、重复的代码、甚至重复的运行环境。对于这一问题,我们需要进行一些优化,如使用共享依赖、根据业务需求选择适当的加载策略等。
6.1.2 架构复杂性
微前端架构需要对系统进行适当的拆分和组织,这可能增加了初始的构建复杂性。因此需要进行合适的架构规划和技术选型,以确保各个微前端应用之间的协作和集成顺利进行。
6.1.3 跨应用通信
在微前端架构中,不同的应用程序之间需要进行跨域通信,以实现共享数据和协作。这就需要我们考虑跨域通信的安全性和效率,并选择合适的通信机制。
6.1.4 版本控制和发布策略
微前端架构中的不同微前端应用程序可能具有不同的发布和版本控制策略。管理多个应用程序的版本控制和升级过程可能变得复杂,需要确保不同版本的应用程序之间的兼容性和一致性。
6.2 当前的争议点
部分网友的争议点:
网友 1:听说这个微前端是内卷出来的,iframe 完全可以作为代替方案。。。嗐
网友 2:一个整用多个 spa 框架实现再反向代理就已经很冗余了,如果还非要把多个框架塞到一个页面里,开发成本和维护成本和所谓的微前端应该如何取舍已经不言而喻了吧。
网友 3: 如果浏览器再出个 iframe 优化方案 ,以后是不是可以再次回归 iframe
网友 4:对小型的项目,小规模应用的场景,用 iframe 就很不错。也没必要别人上微前端,我们就是跟着用。
网友 5:纯粹为了技术而技术,根本不考虑这个沟通和协作成本的问题。。。。其实包括后端的微服务架构,又有多少项目不是为了微服务而微服务呢。
网友 6:说白了就是拿了工资需要玩出点新鲜花样好增加业绩和经验 这些东西都是后端玩剩下的 只不过搬到前端来用了 大前端才是趋势 多端统一 微前端没出息的一种概念罢了
网友 7:微前端对一些大型应用开发很有好处的,上百人的开发团队,几十个子系统,每个小组负责两三个子系统即可,团队小的不适用,本质是为了解决存量、巨型应用的维护和迭代问题
网友 8:我们团队从 2017 年开始就是微前端方案,只是一直是基于 iframe 的而已,最近才起项目打算切成单页应用模式,仔细梳理了一下,成本居然不是很高……
网友 9:必须要支持下,此次我们 app 运营系统的改造就运用了这项技术。那可是一套已经拥有 100 多个业务功能的 java web 项目,已经无法与时俱进了,后续可以愉快的使用当下流行的技术栈来开发新的功能
网友 10:你的业务是否真的需要微前端?市面上那么多框架如何选择?汗~
网友 11:很多评论充斥着对微前端的奚落与偏见,没有遇到这种棘手问题是种幸运。 如果纯粹为了用某个技术而刻意使用,就会导致这种不理解与偏见。这往往是因为教程内容并没有很好解释该技术要解决的问题与应用场景(可能写文章的人自己都不知道),从而产生了这种本可以避免的偏见。
从上述网友的观点来看,关于微前端的争议主要集中在以下几个方向:
「必要性」: 是否所有的前端项目都需要使用微前端?是否有其他更好、更简单的解决方案,比如 iframe?
「开发和维护成本」: 微前端的开发和维护成本是否比传统的单体前端更高?
「技术选择」: 在众多的微前端框架中,应该如何选择?
「团队协作」: 微前端是否真的能够提高团队的协作效率?
「业务规模和复杂度」: 微前端是否只适合大型和复杂的前端项目?
「技术趋势」: 微前端是否只是一种过度的技术概念,或者是一种短暂的技术趋势?
针对这些争议,我的建议如下:
「1. 考虑业务需求」:在决定是否使用微前端时,应首先考虑业务需求。如果你的项目规模小,或者团队人员少,或者技术栈统一,那么可能并不需要使用微前端。反之,如果你的项目规模大,团队人员多,或者需要使用多种技术栈,那么微前端可能会是一个好的选择。
「2. 权衡成本」:实施微前端架构需要投入一定的时间和人力,你需要权衡这些成本和微前端所能带来的好处。
「3. 选择适合的框架」:在选择微前端框架时,应该考虑框架的成熟度、社区活跃度、文档质量等因素。
「4. 提升团队协作」:微前端架构可以帮助提升团队的协作效率,但这也需要团队成员有一定的技术能力和良好的协作精神。
「5. 关注技术趋势」:作为一种新的架构模式,微前端具有一定的前瞻性,值得我们关注和学习。
总的来说,微前端是一种思维转变,它提出了一种新的前端架构模式,旨在解决大型复杂应用的开发和管理问题。然而,这并不意味着微前端适用于所有的前端项目,也并不意味着微前端是一种银弹。如同任何技术或架构模式,微前端也有其适用的场景和限制。我们应该根据实际的业务需求和团队能力,理性地对待微前端,适时地运用微前端。
7 微前端的最佳实践与经验总结
微前端架构,作为一种新兴的前端架构模式,对于如何合理评估、精准选择和高效实现提出了新的挑战。接下来,我们将分享一些在实践过程中积累的经验和最佳实践:
「设计合理的应用拆分策略」:微前端架构强调把大型单体应用拆分成一系列的微应用。这个过程并非随意进行,而需要根据实际业务需求和团队结构进行合理划分。可以按照业务线进行划分,也可以根据功能模块或者变更频率进行拆分。最关键的是要确保每个微应用具有明确的业务领域和责任分界,以避免过度拆分或过度集成。
「确保子应用的独立性与完整性」:每个微应用应具备独立运行与开发的能力。这意味着,应该避免微应用之间的过度依赖,确保每个微应用在必要时可以独立进行开发、测试和部署。这样,不仅保障了项目的可扩展性与可维护性,也为团队提供了更大的开发自由度和灵活性。
「维护统一的用户体验」:尽管每个微应用可以按照自己的节奏进行开发和部署,但在用户体验方面,必须保持一致性。所有的微应用需要遵循统一的样式、交互和动画等设计规范,以确保用户在使用过程中感受到的是一个连贯和一致的系统,而非杂乱无章的应用集群。
「提供公共的基础设施和服务」:在微前端架构中,常会有一些跨应用的通用服务和功能,如认证、权限管理、日志记录、数据访问等。这些功能应当被抽象并封装为公共的基础设施和服务,供所有微应用调用。这样做可以避免代码的重复开发和维护,提高开发效率,同时也保证了系统的一致性和稳定性。
「设计并实施合理的错误处理和容错机制」:在微前端架构中,我们需要面对的一个挑战是如何处理和隔离错误。理想的情况是,当一个微应用出现错误时,这个错误应当被有效地隔离,不会影响到其他微应用的正常运行。为此,我们需要设计并实施一套合理的错误处理和容错机制,以保证系统的健壮性和稳定性。
总的来说,**微前端架构的实践是一场寻求平衡的旅程
**,我们需要在微观与宏观、独立与集成、灵活与稳定之间找到恰当的平衡点。只有这样,我们才能充分发挥微前端架构的优势,同时避免其可能带来的问题。随着前端技术的不断变化和发展,微前端架构也在不断地演化和完善。因此,开发者应关注和学习微前端架构的最新发展,掌握其实现原理和最佳实践,以应对日益复杂的 Web 应用需求,提高项目的可维护性和开发效率。
Reference
[1]
single-spa: https://zh-hans.single-spa.js.org/
[2]qiankun: https://qiankun.umijs.org/zh/guide
[3]icestark: https://github.com/ice-lab/icestark
[4]micro-app: https://micro-zoe.github.io/micro-app/docs.html#/
[5]wujie: about:blank
[6]12.6k: https://github.com/single-spa/single-spa/stargazers
[7]14.8k: https://github.com/umijs/qiankun/stargazers
[8]1.9k: https://github.com/ice-lab/icestark/stargazers
[9]4.5k: https://github.com/micro-zoe/micro-app/stargazers
[10]3.2k: https://github.com/Tencent/wujie/stargazers
[11]链接: https://juejin.cn/post/6844904202389438478#heading-30
[12]链接: https://juejin.cn/post/6844904194818703374
[13]链接: https://developer.aliyun.com/article/742576
[14]链接: https://tech.meituan.com/2020/02/27/meituan-waimai-micro-frontends-practice.html