微前端

🎗 本文节选自 Web 开发导论/微前端与大前端,着眼阐述了微服务与微前端的设计理念以及微前端的潜在可行方案,需要致敬的是,本文的很多考虑借鉴了 Phodal 关于微前端的系列讨论以及 Web Architecture Links 中声明的其他文章,此外结合了自己浅薄的考量与实践体悟,框架代码可以参阅 Ueact/micro-frontend

微前端

所谓微前端,它可以看作是一种前端架构风格,即是一种多个团队通过独立发布功能的方式来共同构建现代化 Web 应用的技术手段及方法策略。它提倡由可独立交付(开发/部署)的前端应用组合成一个更大的整体前端应用。

微服务与微前端,都是希望将某个单一的单体应用,转化为多个可以独立运行、独立开发、独立部署、独立维护的服务或者应用的聚合,从而满足业务快速变化及分布式多团队并行开发的需求,并且保证仍然聚合为一个产品出现在客户面前。如康威定律(Conway’s Law)所言,任何组织在设计一套系统(广义概念)时,所交付的设计方案在结构上都与该组织的通信结构保持一致;微服务与微前端不仅仅是技术架构的变化,还包含了组织方式、沟通方式的变化。微服务与微前端原理和软件工程,面向对象设计中的原理同样相通,都是遵循单一职责(Single Responsibility)、关注分离(Separation of Concerns)、模块化(Modularity)与分而治之(Divide & Conquer)等基本的原则。

image

在某些场景下,微前端也包含了对于系统的纵向切分;即不同的团队会负责系统中某个特性/模块,从数据库、服务端到用户界面完整的流线。每个团队会更多地着眼于业务模型与特点。独立并不意味着完全的切割,各个特性/模块之间的共现组件可以通过 NPM/Git Submodule/Dynamic Load/Module Federation 等方式进行协同开发与复用。值得留意的几个点:

  • 微前端不是一门具体的技术,而是整合了技术、策略和方法,可能会以脚手架、辅助插件和规范约束这种生态圈形式展示出来,是一种宏观上的架构。这种架构目前有多种方案,都有利弊之处,但只要适用当前业务场景的就是好方案。
  • 微前端并没有技术栈的约束。每一套微前端方案的设计,都是基于实际需求出发。如果是多团队统一使用了 react 技术栈,可能对微前端方案的跨技术栈使用并没有要求;如果是多团队同时使用了 react 和 vue 技术栈,可能就对微前端的跨技术栈要求比较高。

微前端特性

假设要做一个食品外卖的网站。乍一看这种网站好像很好做,但想要做好需要在诸多细节上下足功夫:

  • 应该有一个引导页面,让顾客浏览并搜索餐馆。顾客应该能按照一系列参数(包括价格、菜品或订购历史等)来搜索并过滤餐馆。
  • 每家餐馆都要有自己的页面,页面中要展示菜单,允许客户自主选餐,还要有折扣、套餐和特殊要求选项。
  • 顾客应该有自己的主页,可以用来查看订单历史、跟踪外卖进度并自定义付款选项

每个页面都非常复杂,都应该分配一个专门团队来负责,并且每个团队都应该有足够的独立性。各个团队都应该能独立开发、测试、部署和维护自己的代码,而不会与其他团队发生冲突或需要其他团队配合。但在客户这里,整个网站仍然应该是一个无缝的整体。

独立与自治

只有在应用开发整体流程中能独立开发、独立部署和独立自治代码库等,那么这个前端项目才能具有真正的独立性保证;而这种团队自治的可能性也恰好因契合了 Conway’s Law 提到的“设计系统的架构受制于产生这些设计的组织的沟通结构”,从而也带来了可能的组织管理形式新变革。

自治团队

解耦代码库、分离发布周期还能带来一个高层次的好处,那就是大幅提升团队的独立性;一支独立的团队可以自主完成从产品构思到最终发布的完整流程,有足够的能力独立向客户交付价值,从而可以更快、更高效地工作。为了实现这一目标需要围绕垂直业务功能,而非技术功能来打造团队。一种简单的方法是根据最终用户将看到的内容来划分产品模块,让每个微前端都封装应用的某个页面,并分配给一个团队完整负责。相比围绕技术或“横向”问题(如样式、表单或验证)打造的团队相比,这种团队能有更高的凝聚力。

独立部署

就像微服务一样,微前端的一大优势就是可独立部署的能力。这种能力会缩减每次部署涉及的范围,从而降低了风险。不管你的前端代码是在哪里托管,怎样托管,各个微前端都应该有自己的持续交付管道;这些管道可以将微前端构建、测试并部署到生产环境中。我们在部署各个微前端时几乎不用考虑其他代码库或管道的状态;就算旧的单体架构采用了固定、手动的按季发布周期,或者隔壁的团队在他们的主分支里塞进了一个半成品或失败的功能,也不影响我们的工作。如果某个微前端已准备好投入生产,那么它就能顺利变为产品,且这一过程完全由开发和维护它的团队主导。

增量升级

对于许多组织来说,追求增量升级就是他们迈向微前端的第一步。对他们来说,老式的大型单体前端要么是用老旧的技术栈打造的,要么就充斥着匆忙写成的代码,已经到了该重写整个前端的时候了。一次性重写整个系统风险很大,我们更倾向一点一点换掉老的应用,同时在不受单体架构拖累的前提下为客户不断提供新功能。

为了做到这一点,解决方案往往就是微前端架构了。一旦某个团队掌握了在几乎不影响旧世界的同时为生产环境引入新功能的诀窍,其他团队就会纷纷效仿。现有代码仍然需要继续维护下去,但在某些情况下还要继续添加新功能,现在总算有了解决方案。

到最后,我们就能更随心所欲地改动产品的各个部分,并逐渐升级我们的架构、依赖关系和用户体验。当主框架发生重大变化时每个微前端模块都可以按需升级,不需要整体下线或一次性升级所有内容。如果我们想要尝试新的技术或互动模式,也能在隔离度更好的环境下做试验。

面向扩展的代码组织

技术栈无限定

微前端的技术栈自主性有利于多个不同技术栈的团队协同合作;同时技术栈的可平滑迁移也对旧有业务的不断迭代和技术升级带来较大的便利性。

运行时集成

在现代前端开发流程中,我们最常见的往往是构建时的复用集成;反倒是前端早期时,运行时复用的模块分离得更独立;而微前端也恰好能整合好这样的微模块概念,并保持这样模块的独立性和依赖共享。

颗粒化解耦与可组合

在大型的前端工程中,我们对于颗粒化解耦有很高的要求,常基于不同纬度划分,例如业务类型颗粒化、技术服务类型颗粒化等等。各个微前端颗粒的可组合性又让多个可交付系列产品有很好的颗粒一致性和整体定制差异化,并能极大减少业务重复开发的资源浪费。

微前端体系下,每个小模块的代码库要比一个单体前端的代码库小很多。对开发者来说这些较小的代码库处理起来更简单方便。而且微前端还能避免无关组件之间不必要的耦合,让代码更简洁。我们可以在应用的限界上下文(详见下方链接)处划出更明显的界限,更好地避免无意间造成的这类耦合问题。

当然,只靠架构更迭本身(比如说“我们改成微前端吧”)并不能自动为以往的优质代码生成替代品。我们要做的是设法让糟糕的决策难以露头,而让正确的决策畅通无阻,从而进入迈向成功的良性循环。例如,现在很难跨越限界上下文共享域模型,所以开发者就不太可能这样做了。类似地,微前端会让开发者更审慎地把握数据和事件在应用的各个部分之间流动的方式,其实就算没有微前端我们本来也应该这样做的!

Links