微前端资料
我认为比较好的微前端思想技术体系文献资料:
参考资料:https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-1.md
原文资料:https://martinfowler.com/articles/micro-frontends.html
文章大量引用了上述文献资料的第一部分,对许多翻译生硬的地方加以润色,难理解的点增加注释,并穿插加入一些个人观点。
引言
众所周知,前端的世界瞬息万变。新的语言,框架库,思想层出不穷,让人应接不暇。其中许多了解一下即可,但是对去现在比较火热的“微前端(MircroFrontends)”,还是值得去了解和研究一下的。大家对于前端的不断探索研究甚至创造,我觉得无非两个目的。一是尝试拓展前端的边界(创造更多可能);二是前端已有领域的不断优化(效率,维护性,质量,管理成本)。很明显,微前端是后者。那接下来,我们了解一下什么是微前端,探讨一下它如何解决和优化前端领域一些问题的。
什么是微前端
微前端首先是一种(管理)思想。将前端整体分解为小而简单模块的一种模式。这些块可以独立开发、测试和部署。
与此同时,我们仍然需要把模块聚合为一个产品出现在客户面前。我们将这种技术也称为微前端。
所以微前端是一种管理思想和集成技术结合的产物,我们将其定义为:
一种将多个可独立交付的小型前端应用聚合为一个整体的架构风格
简而言之,微前端都是将巨大的东西分成更小、更易于管理的小部分,然后明确它们之间的依赖关系。我们的技术选择、代码库、团队以及发布流程都应该能够彼此独立地运行和开发,不需要过多的协调。我认为好的微前端要做好两件事情(微前端也分两步):
- 分解(业务层面):保证业务的合理拆分,并理清它们直接的依赖交互关系。
- 聚合(技术层面):架构层面,要在技术上做到很好的聚合,保证彼此独立,不相互污染,又能合理通信。
使用场景主要有:
- 拆分巨型应用,使应用变得更加可维护。
- 兼容历史应用,实现增量开发。
优点
- 体积小、易拼合且易于维护的代码库
- 更具扩展性的互相解耦且独立的团队
- 和以前相比能采用增量的方式,更易于对前端的某些部分进行升级、更新甚至重写
增量升级,逐步翻新。
很多公司都普遍存在一个历史问题:过时的技术栈,赶工完成的代码质量,五花八门的代码风格。随着员工的更迭,新功能的不断堆积,后人维
护起来,看不懂,改不动,只想重写的情况日益严重。这个时候,你才明白,长江后浪推前浪,前浪原来是被后浪咒死在沙滩上的。如果这个时候,你发现这居然还是一个巨石应用……后浪也想在沙滩上自杀了。
当然了,喜欢折腾的前端没那么脆弱,当然还是要解决这个问题。我相信,这个时候就已经诞生了微前端,或者说应用了微前端的管理思想-细
分:随着新的功能,将业务逐步在多个新应用中重构翻新。当然,也有在原应用中或者在一个新应用中想办法逐步翻新,这基本上是重蹈覆辙。
简单、解耦的代码库
每个单独的微前端项目的源代码库,会远远小于一个单体前端项目的源代码库。这些小的代码库将会更易于开发。更值得一提的是,我们避免了不相关联的组件之间无意造成的不适当的耦合。通过增强应用程序的边界来减少这种意外耦合的情况的出现(领域驱动设计(DDD)中抽象的限界上下文(BoundedContext)概念,不懂也不用太纠结)。
当然了,一个独立的、高级的架构方式(例如微前端),不是用来取代规范整洁的优秀老代码的。我们不是想要逃避代码优化和代码质量提升。相反,我们降低做出错误决策的可能,增加正确决策的几率,从而使我们Falling Into The Pit of Success进入成功之坑(该思想大致的意思是说设计者要通过精心设计的系统或者API,让用户做正确的事变的容易,而不过于为容易犯错而苦恼。用户犯错,那就是设计者的错)。微前端会促使您明确并慎重地了解数据和事件如何在应用程序的不同部分之间传递(跨应用通信),这本是我们(微前端架构设计师)早就应该开始做的事情!
独立部署
与微服务一样,微前端的独立可部署性是关键。它减少了部署的范围,从而降低了相关风险。无论您的前端代码在何处托管,每个微前端都应该有自己的连续交付通道,该通道可以构建、测试并将其一直部署到生产环境中。我们应当能够在不考虑其他代码库或者是通道的情况下来部署每个微服务。
做到即使原来的单体项目是固定的按照季度手动发布版本,或者其他团队提交了未完成的或者是有问题的代码到他们的主分支上,也不会对当前项目产生影响。如果一个微前端项目已准备好投入生产,它应该具备这种能力,而决定权就在构建并且维护它的团队手中。
图 2 : 每个微前端都独立的部署到生产环境上
自主的团队
将我们的代码库和发布周期分离的更高阶的好处,是使我们拥有了完全独立的团队,可以参与到自己产品的构思、生产及后续的过程。每个团队都拥有为客户提供价值所需的全部资源,这就使得他们可以快速且有效地行动。为了达到这个目的,我们的团队需要根据业务功能纵向地划分,而不是根据技术种类。一种简单的方法是根据最终用户将看到的内容来分割产品,因此每个微前端都封装了应用程序的单个页面,并由一个团队全权负责。与根据技术种类或“横向”关注点(如样式、表单或验证)来组成团队相比,这会使得团队工作更有凝聚力。
图 3:每个应用都由一个团队负责
缺点
负载体积
独立构建,会造成公共依赖的重复,增加了用户所需下载依赖的体积。
一种解决方案是将我们编译后代码的常见依赖外置(如下代码)。一旦我们沿着这条路走下去,我们将重新引入一些微前端之间构建过程的耦合。现在它们之间有着一个隐含的合约:“我们都必须使用这些依赖的明确版本”。如果其中一个依赖产生重大改动,我们可能最终需要一个大的协调升级工作以及一次性的同步发版。这是我们使用微前端最初想要避免的一切。
但并不全是坏消息。首先,即便我们对于重复的依赖不采取任何措施,每个单独页面仍可能比我们构建整个前端更快地加载。原因是通过独立编译每个页面,我们有效地以我们自己的形式实现了代码分割。在传统的前端中,应用中的任何页面加载完成时,我们通常会一次性下载所有页面的源码和依赖。通过独立构建,任何单独的页面加载将只会下载那个页面的源码和依赖。这可能导致更快的首页加载,但随后的导航速度会变慢,因为用户必须在每个页面上重新下载相同的依赖。如果我们严格地不用不必要的依赖使我们的微前端膨胀,或者我们知道用户在应用中通常访问的一两个页面,即便有重复依赖,我们也很可能在性能方面达到净增益。
在前一段有很多“可能”和“也许”,表明了每个应用通常都有它们自己独特的性能特征。如果你想确切地知道特定的变化会造成什么性能影响,只能靠实际测量,而且最好是在生产环境中。我们见过很多团队仅仅为了下载数兆大小的高清图像或者对一个运行非常慢的数据库进行昂贵的查询额外多写几千字节的 JavaScript 代码。因此,尽管考虑每个架构决策的性能影响很重要,但请确保你知道真正的瓶颈在哪里。
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script src="%REACT_APP_CONTENT_HOST%/react.prod-16.8.6.min.js"></script>
<script src="%REACT_APP_CONTENT_HOST%/react-dom.prod-16.8.6.min.js"></script>
</body>
环境差异
我们应该能够开发一个单一的微前端,而无需考虑其他团队正在开发的所有其它微前端。我们可能甚至应该在“独立”模式下,在空白页面上运行我们的微前端,而不是运行在将在生产环境中承载微前端的容器应用内部。这可以使开发变得更加简单,特别是当真正的容器是一个复杂的遗留代码库的时候,而通常情况下我们使用微前端来逐步从旧世界迁移到新世界。但是,在与生产环境完全不同的环境中开发存在风险。如果我们的开发时容器与生产容器的行为不同,那么我们可能会发现我们的微前端被破坏,或者在我们部署到生产环境时表现不同。特别值得关注的是可能由容器或其他微前端带来的全局样式。
这里的解决方案与我们不得不担心环境差异的任何其他情况没有什么不同。如果我们在一个与生产环境不同的本地环境开发,我们需要确保定期将我们的微前端集成和部署到像生产环境的环境中,并且我们应该在这些环境中进行测试(手动以及自动化)以尽早发现集成问题。这不会完全解决问题,但最终这是一个取舍:简化开发环境的生产力提升是否值得冒集成出问题的风险?答案取决于项目!
运维复杂度
最后的缺点是与微服务直接平行的缺点。作为一个更加分散的架构,微前端将不可避免地导致需要管理更多的东西 —— 更多的存储库,更多的工具,更多的构建/部署管道,更多的服务器,更多的域等等。因此,在采用这样的架构之前,你应该考虑几个问题:
- 你是否有足够的自动化可行地提供以及管理额外所需的基础设施?
- 你的前端开发、测试和发布进程是否会扩展到许多应用中?
- 你是否对围绕工具和开发的实践变得更加分散且不易控制的决策感到满意?
- 你将如何确保你的多个独立前端代码库中的最低代码质量,一致性或代码管理?
我们可能会另写一篇文章讨论这些主题。我们希望提出的主要观点是,当你选择微前端时,根据定义,你选择创建许多小东西而不是一个整体。你应该考虑你是否有采用这种方法所需的技术和组织成熟度,从而不造成混乱。
技术点
主要难点在于集成方式(公共依赖)和跨应用通信。有兴趣可以研读文章开始的参考资料2-4章节。