利用微前端扩展应用程序
这篇博文总结了我于 2020 年 12 月 17 日在 Archimydes Mastermind 活动上所作的演讲。
与后端相比,现代 Web 应用程序的前端往往复杂且功能繁多。
由于框架和编程范式种类繁多,构建一个可扩展的、一致的前端是一个极具挑战性的难题。本文将介绍如何利用微前端设计模式来扩展前端应用程序和开发团队。
首先,我将介绍微前端模式。然后,我们将探讨启动微前端项目时需要做出的一些关键决策。最后,我们将了解这种模式在哪些情况下行之有效。
1. 扩展应用程序
一般来说,应用程序扩展是指扩展后端应用程序以服务不断增长的用户数量。通常,这涉及到如何:
- 提升绩效
- 降低延迟
- 维持负荷
- 控制计算成本
这些参数通常都适用于后端应用程序。
对于前端应用,我们通常只需要一个优秀的 CDN 来高效地分发静态资源。然而,
前端应用程序的扩展也关乎开发团队的扩展,包括规模扩展(单个团队的规模扩大)或团队数量扩展(多个团队)。
此外,应用程序越来越依赖前端的原因在于:
- 后端部署和启动变得越来越容易
- 终端用户计算能力正变得越来越便宜、越来越强大。
- 更多功能正被推送至终端用户界面和设备。
因此,产品团队需要找到一种高效的方式,在多个开发团队大规模协作的情况下构建和交付前端应用程序。产品团队需要在执行此操作的同时,减少开发过程中的瓶颈。
2. 单体架构、微服务和微前端
单体架构并非糟糕的设计选择。
开发任何应用程序时,最好始终采用单体架构。预先划分模块边界非常困难,而且容易出错。随着应用程序的增长,最好再确定模块边界并将其拆分。
微服务
从单体架构向微服务架构演进,是最佳选择。这样我们就能保证:
- 清晰的模块边界
- 独立部署
- 多语言开发和技术多样性
然而,我见过的大多数微服务都是这样的。
独立部署!== 独立发布
团队可以独立开发和部署后端。但是,他们需要等待前端开发和部署完成后才能进行后端开发和部署。
微前端的出现
微前端其实就是将微服务的概念应用到前端。它将应用程序的前端进行切片,使其符合后端模块的边界,并创建端到端独立的发布路径。
微服务的所有承诺 + 独立发布
微前端的优势
- 独立团队
- 独立发行
- 简单、解耦的代码库
- 渐进式升级
需要解决的问题
- “共享还是不共享? ”——代码复用性是软件开发中最被高估的原则之一。复用性的问题常常被忽视或未被讨论。在采用微前端架构时,团队之间需要讨论这个问题。一开始,先复制一份代码的策略是有效的,因为它能让团队在初期更快地执行。
- 应用加载性能 ——微前端可能会影响应用的加载性能。虽然有一些方法可以缓解这种影响,但必须考虑所需的工作量。
- 应用程序设计一致性——参与应用程序开发的人员越多,就越容易出现不一致的情况。虽然有一些方法可以缓解这个问题,但需要考虑缓解措施所需的工作量。
3. 微前端开发中的关键决策
让我们来探讨一下在微前端应用开发初期需要做出的一些重要决策。我将尝试介绍我们在与分布在三个地区的分布式团队合作,历时两年构建应用时所采取的解决方案。这些决策会根据项目的具体情况而有所不同,但无论如何,这些问题都需要解决。
为了解释所面临的挑战和做出的决定,我将以以下用例为例进行说明:
开发一款应用程序,允许用户配置和购买笔记本电脑。类似于苹果公司的同类产品。
用户可以根据需要配置笔记本电脑的各种组件、配件、保护计划等。用户应该能够搜索配件或内置型号,最终订购产品并获得交付。
除了配置、搜索和订购这 3 项服务之外,我还会提供另一项名为“框架”的服务,仅仅是为了将应用程序整合在一起。
- 框架:一个与业务逻辑无关的编排服务,它知道如何下载其余服务的前端。
A) 将多个前端组合成一个应用程序
最终用户并不关心所使用的技术。他们的体验不应受到技术的影响。
将多个前端组合成一个应用程序是选择微前端时需要解决的首要问题之一。
前端设计
我们可以通过两种方式实现这种构图,让我们来分析一下这些方法的优缺点。
构建时合成与运行时合成
构建时组合是指将多个前端应用程序构建成一个大型应用程序并进行部署。这可以通过使用 npm 或 yarn 包来实现。
优点:
- 良好的依赖管理,从而生成更小的软件包。
- 独立跨团队发展
缺点:
- 由不同团队构建的单体建筑
- 非原子部署
运行时合成是指当用户请求页面时,前端直接集成到浏览器中。这可以在“服务器端”或“客户端”完成。
优点:
- 独立团队,独立部署
- 原子部署,因此不存在版本问题
缺点:
- 客户端 API 请求过多(?),导致数据包大小增加
运行时组合的工具包选项
服务器端:
- SSI(服务器端包含)
- 裁缝(来自 Zalando)
客户端:
- JSPM
- SystemJS
- FrintJS
- 单人水疗池
我们为这个项目选择了运行时合成。由于我们的应用是在客户端渲染的,所以实现起来比较简单。
B) 前端之间的通信
多个前端需要彼此共享数据。虽然这种共享应该尽可能减少,但却无法避免。以下是一些实现方法:
- 状态管理工具
应用程序内有一个全局存储库,所有前端都使用同一个库来访问该存储库。
- 窗口事件
另一种方法是利用窗口(DOM)事件功能。以下是一个示例事件。
我们过去通过通用的 Redux store 和 Redux events 进行通信,因为我们微前端中的所有应用程序都使用 Redux。
C) 设计一致性
设计一致性是其中最难解决的问题之一。
我们团队通过组建小组来应对这个挑战。假设有三个小组,每个小组都分配了一名设计师。
我们组建了一个由所有设计师和一些感兴趣的开发人员组成的公会。他们构成了一个虚拟团队。他们负责所有设计决策,并确保各自的团队遵守核心设计原则。
最初,公会为该应用程序创建了一个样式指南。主要是 CSS,应用程序团队直接从样式指南中复制粘贴代码来构建组件。
随着功能的不断开发,我们开始提取高阶 JavaScript 组件并使其可共享。这更像是一种演进过程,只有在拥有稳定的设计系统后才能有效运作。
而且,由于各个团队都使用了相同的前端框架(React),因此我们构建这个组件库也变得更加容易。
D) 测试策略
决定“如何测试”至关重要。因为这是一种相对较新的范式,而且应用程序中有很多动态组件。
我们将主要讨论测试策略中的“集成测试”和“功能测试”,因为“单元测试”的执行方式不会有太大区别。
- 集成测试
轻量级的“消费者驱动合同”(CDC)对我们帮助很大。
CDC(消费者服务测试)是指消费者服务对提供者服务进行测试的地方。提供者必须先运行所有消费者服务,然后才能发布部署组件。
这不需要非常复杂,使用一些轻量级的方案就能快速完成,无需借助任何大型框架。但具体情况具体分析。
在我们的场景中,Frame 是所有服务的消费者,它与所有服务提供者共享一个简单的 JSON 契约和一个小型 JS 测试。这确保了当服务自动部署时,Frame 本身不会出现问题。
- 功能测试
这是我最不喜欢的测试方法之一,不过,就像科技领域的其他事物一样,它也有一些坚定的支持者和追随者。就我们而言,我们只使用 Selenium 对少数几个关键且成功的用户流程进行了自动化功能测试。
这些旅程涉及多个服务,因此开发和维护难度更大。我经常收到的一些关于这些测试的常见问题包括:
常见问题解答
- 功能测试的所有权归谁?
答:产品团队和业务分析师。他们负责定义自动化场景。
- 谁来编写功能测试?
答:由所有团队的QA和一些开发人员组成的公会。
- 谁来修复功能测试?
答:破解它的团队。
何时应该选择微前端?
微前端并不适合所有人。它会显著增加开发和维护成本。
- A. 分布式独立团队,需要并行化
如果您的开发团队不在同一地点办公,并且需要进行相当多的并行化工作,那么这可能是实施微前端的一个理由。
- B. 与前端的不同框架进行协作
想象一下,你正在接手一个遗留应用程序,但想要使用现代设计元素构建新功能,那么微前端可以为你提供良好的开端。
- C. 拥有构建微服务应用程序经验,并愿意将其推进到下一个阶段的团队
这里提到的大部分观点都是具有前瞻性的实践。微前端需要对领域有扎实的理解,并且需要有良好的自律性,才能将工作控制在自己的职责范围内。
最后,值得记住的是:
这不是短跑,这是马拉松。
微前端会给整个应用程序增加显著的开销。对于小型应用程序或由单个团队构建和管理的应用程序来说,这并非理想之选。只有当您准备好与多个团队进行长期合作时,上述挑战才值得解决。
文章来源:https://dev.to/prasann/scaling-applications-using-micro-frontends-4kgn













