我不懂微前端
昨天,在和我的狗一起散步回来之后,我在推特上看到了一些通知,人们艾特我,要求我分享对 Dan Abramov 关于微前端 的想法:
如果你关注了我,你就知道我对微前端非常热衷,我和他们一起工作了一段时间,并且也保持着开放的心态,分析不同的方式,来理解他们的利弊。
如果你没有关注我,而是从技术角度对这个主题感到好奇,请查看我的 Medium 主页,除此之外,还有许多其他的关于微前端的资源,只需在 Medium 搜索或使用你最喜欢的搜索引擎就好。
我没法涵盖 Twitter 中讨论的所有主题,但让我们从头开始,看看我是否可以帮助添加一些 上下文(在本文中你会越来越多地听到这个词😁)微前端话题。
免责声明
首先,我不是通过写这篇文章来指责或攻击任何人,甚至不是为了在社交平台上引战,我尊重任何观点,有时候我和其他人有同样的观点,有时候也没有,这种行为带来了创新和新想法,所以我完全赞同它。
考虑到有些人是由丹开始的推文提到我名字的,所以我想分享我的想法,因为我真的相信我们可以真正讨论微前端,为每个人提供我在每周收到的常见问题的好处。社交,个人电子邮件,演讲后等等。
其他人就上述推文与我取得了联系:我没有直接回复推文,因为讨论一个有趣的话题的话,像 280 个字符中的主题实际上是限制性的,容易被误解或省略一些重要的细节。
为什么是微前端而不是良好的组件模型?
组件绝对是有效的解决方案,许多公司每天都在使用它们,并取得巨大成功,但它们并不是万能的灵丹妙药;项目,团队,业务,更一般来说,它们的上下文必须适合,否则我们就像试图将一个正方形放入一个圆一样,我们并不总是能符合预期的。
探索新的可能性,挑战我们的信念和“标准的做事方式”,推动我们的行业向前发展,巩固现有标准或引入新标准。
让我们从这开始,微前端并没有试图取代组件,我们有可能没有适合所有项目的东西,比如组件,它就不是银弹。
使用正确的工具来做正确的工作,这应该是我们的目标。
请记住,开发软件是经验性的,而不是科学的。
我曾见过大型组织里,有着可怕的代码库和一个实际成功的产品,我也看到过完全相反的情况,我们不能只看到硬币的一面。
到目前为止,我只尝试过大规模的微前端(大约200人 - 前端和后端工程师 - 在同一个项目上工作),结合微服务和团队所有权,与我们公司以前的模型相比,效果非常好。
他们能在较小的项目中工作吗?理想情况下可以,但我想先尝试一下。
在理论上,一切看起来都很好,当它进入到细节中时,你就会意识到局限性并找到新的挑战。如果您有任何经验,我很乐意听取您的意见!
关于微前端,有不同的风格,例如,我们可以使用 iframe 来组成最终的视图,或者使用 Edge Side Include 或 Client Side Include,甚至使用预渲染策略,如 Open Components 或者 Interface Framework 并将结果缓存在 CDN 级别上。
另一种方法是使用一个协调器(orchestrator),它正在为 SPA,单个 HTML 页面或 SSR 应用程序提供服务,协调器可以处于边缘,起点或客户端,协调器的一个例子可以是单个-SPA。
这些方法表明我们有两种主要方法来确定微前端的大小:
- 用户界面的一部分,可以对应一个组件,但不必与组件是 1对1 的映射关系
- 整个业务域,可以对应 SPA,单个 HTML 页面或 SSR 应用程序
它们有各自的利弊,我个人更喜欢后者,但它也不是防弹的,了解每种方法的局限性,以及这些限制是否会影响最终项目结果非常重要。
微前端绝对是一种影响你所在组织的技术,它可以提供团队之间的隔离,避免过多的集中化,并使团队能够采取 局部决策 。
这并不意味着这些团队就无法追求战略或 API 约定达成一致意见,微前端使团队能够采取可以遵循的路径,而无需与其他团队协调每一项技术决策,这些决策可能会影响代码库,但我们允许它们快速失败,独立构建和部署,遵循组织定义的某些边界(公司支持的语言,最佳实践等)。
我个人在不同的组织中工作过,新的加入者会提供关于如何更改“核心库”但通常用于政治的好的见解,因为这些改变不会立即带来好处,这些建议在积压的内部停留在等待状态中。
将团队决策权下放可能是公司可以做的最好的事情之一,因为这个团队与产品团队和业务专家共同生活,每天都在说同一种语言,他们处于博弈的领先地位,集中化反而输了上下文,提供的一些约束有时候也是不必要的。
当公司能够在特定业务领域内提供一些技术界限时,团队可以以最佳方式表达自己,可能会犯一些错误,但是恢复速度非常快,因为要改变的工作范围小于完整的应用程序。
理解上下文
丹在他的例子中是正确的,但他没有考虑谈话的背景,他试图推广一个必须适用于所有事情的解决方案,但实际情况并非如此。
任何决策的背景,最重要的事情,是去理解一个由个人贡献者,团队或组织进行的技术实现。
在过去的十年中,我看到许多项目以类似的方式,相同的架构,相似的模式编写......但是在旅途中遇到了不同的结果和不同的挑战,正如我之前所说,软件开发是经验性的,而不是科学的。
现在,我们可以更好地了解我们可以使用哪些方法成功交付项目,我们不再使用适合所有项目的框架或架构,我们正在尝试使用正确的工具来完成正确的工作。
如果一个项目应该使用高度共享的组件,项目从而成功,那就绝对没问题,可能项目的最终结果,环境,所涉及的参与者以及为交付项目而建立的过程使共享组件库成为了合适的解决方案。
与此同时,其他情境可能需要不同的方法,在盒子外思考,因为传统方法无法提供可预测的结果。
背景是关键,理解业务,我们运营的环境,我们所瞄准的结果都与我们的背景相关联。
因此,拥有一个组件库来抽象数百个(如果不是数千个)组件的功能,就像拥有多个 SPA 一样,代码被复制而不是包含在库或其中的许多中。
上下文迫使我们做出有时不是其他人期望的决定,我们过去已经学到了许多规则/指导,例如 DRY(不要重复自己)或 DIE(复制是邪恶的),这些是完全适用的,但不是我们需要尊重的教条,不管是什么原因。因为有时我们有充分理由那样做。
不要误解我的意思,我不是在提倡复制大量代码是一种最佳实践,但有时候这是一种必要的恶行,它可以更快地向前推进。
代码重复可以使我们的团队更加自主,因为他们不会共享由于抽象而变得更加复杂的代码,并且他们不依赖于外部团队。
与往常一样,我们在复制代码时需要深思熟虑,抽象代码和复制哪个更合理,上下文驱使我们做出决定。
抽象通常比代码重复更昂贵,如果你大规模地应用错误的抽象,我们就会生成复杂的代码,导致团队成员产生挫败感,转而导致更糟糕的行为,例如忽略集中式方法,转而采用更小的“适合目标“ 的方法,由团队在其自己的代码库中实现的方法,其结果是对整体解决方案的控制较少。
所以,是的,让我们避免代码重复,但要在你的决策中保持平衡,因为如果你在应用程序的正确部分解决这些问题,你会感到惊讶。
多个技术栈
我完全同意 Federico 的观点,我们能够选择的想要的任何技术,它们都可能成为灾难的源泉......那如果我们只使用最好的部分呢?
微前端并没有强加不同的技术堆栈,事实上它们支持这种方法,并不意味着我们需要遵循它。
就像在微服务世界中一样,我们在同一系统中最终不会有20种不同的语言,因为它们中的每一种都是固执己见的,并且在系统内部带来了自己的愿景,维护不同的生态系统是非常昂贵的,并且可能会造成混乱,从而不会提供太多的好处。
但是权衡可能会有所帮助,我们可以从中选择有限的语言或框架列表,那些真正起作用的部分。
猛不及防,我们并没有与一个技术栈紧密耦合,我们可以重构支持前一个堆栈的旧项目,以及一个缓慢但稳定地进入生产环境而不需要大爆炸版本的新项目(参考扼杀模式),我们可以在生产环境中使用相同的库或框架的不同版本而不影响整个应用程序,我们可以尝试新的框架或方法,观测到实际的性能在运行,我们可以雇用最好的来自多个社区的人等等许多其他的优势。
当我们具有使用多个技术栈的能力时,有一些准则确实有助于从中获得巨大的好处,只有当存在混乱而不是共同的目标时,缺点或灾难才成为现实。
注意代码包的大小
我对 Dan Shappir 非常敬重,去年我在 Fluent 会议期间参加了在圣何塞举办的研讨会。
他提供了大量关于如何优化我们的 Web 应用程序的好见解,绝对是性能优化的大师。
我认为 Dan 在这里分享的评论确实依赖于(再次强调)上下文,例如,使用微前端并在多个 SPA 中切分应用程序,只允许下载部分应用程序,从应用程序代码库中拆分库,允许我们如果用户需要快速往返(roundtrips),增加服务供应商文件的 CDN 上的 TTL,浏览器也增强了直接从磁盘提供文件的缓存策略,而不是执行多次往返。
最后,服务端的 workers 可以通过针对依赖关系的缓存策略来缓解此问题,如果它对用例是明智的。
现在,无法避免的是,如果我们将依赖关系打包得很糟糕,这会影响加载时间,但它不会影响微前端,它会影响 SPA。
可能与微前端一样,你也可以共享依赖关系(看看单个的 SPA)或者你说不的话,在后一种情况下,取决于你的应用程序使用方式,例如,如果我们能了解用户在我们的应用程序中的行为,我们可以将应用程序“切片”到我们的用户正在消费微前端内部的“旅程”,并在另一个内部开始新的旅程。
我们可以发现的是,我们的用户来到我们的平台执行一次旅程,在这种情况下,他们将只下载微前端中所需的依赖项和代码,而不是整个应用程序中使用的所有依赖项。
同理,用户可以在我们的应用程序中随机导航,这样的话他将多次下载一些依赖项,但在这种情况下,这取决于团队如何减轻他的旅程并改善性能以提供更好的体验。
帕累托法则 (80/20 规则) :“......对于许多事件,大约 80% 的影响来自 20% 的原因”。
使用微前端的 API 管理可能是具有挑战性的,但不比与其他架构协同工作重要,在我们的案例中,我们正在从服务字典中移动,我们列出了所有可用的 API 到专用于每个微前端的列表,它需要一点点更多的工作,但它优化了服务器和客户端之间共享的有效负载,只显示对该微前端感兴趣的 API。
显然,并不总是可以让 API 只与一个微前端相关,在这种情况下,我们需要在团队之间拥有外部依赖关系和沟通,但与日常工作相比,它非常有限。
我想在此强调的是,微前端并不完美,但良好的实践组合可以使我们的项目以高标准交付:正确的工作,正确的工具,还记得吗?
总结
我真的相信,正确的工作使用正确的工具是必不可少的,整体,微服务,组件,库,微前端是表达自己的工具和技术,我们的意图只是“解决方案”的一方面(技术侧),另一方面显然是使用这些技术和工具产生的业务影响。
微前端可以真正帮助组织更快地行动,在业务领域内进行创新并隔离故障,同时我不反对任何形式或形状的整体应用程序,我不(完全)反对集中化,尽管我经常看到任何类型的库都过于优化而没有真正关注业务的发展方向,但却增加了一定程度的无意义抽象,从而降低了开发人员的工作效率,而不是加速它。
由于团队不能过多地影响另一个团队的工作,因此集中化通常会导致团队失望,因为外部依赖很难解决。
我知道有一些方法可以通过内部开源缓解这个问题,我也不能提供太多关于这种方法的见解,但是从我了解到的几个谈话和我有的聊天,对于在公司中使用内部采购的一些工程师而言,如果你有不同代码库的共同责任,这绝对是一个很好的方法,如果你有这方面的经验,请随时评论这篇文章。
采取平衡决策是成功的秘诀。
最后,请记住背景是理解决策的关键。建筑师经常编写ADR(架构决策记录)。这些文件正在帮助公司中的任意员工理解为什么做出描述上下文,可用选项,所选择的选项以及最终由此决定产生的后果的决策。
我经常看到人们判断其他公司或同事的决定,而不理解做出决定的背景,实际上,背景对决策本身更为重要,因为尽管它听起来很可怕或完全不合适,但实际上可能有是该特定背景的最佳(或唯一)选项。
像往常一样,我愿意讨论,我相信人们会不同意这篇文章中分享的一些观点,但这就是分享我们的经验和信念的全部意义! 🤓