paint-brush
打破单体:代码拆分技术综合指南经过@aleksandrguzenko
8,117 讀數
8,117 讀數

打破单体:代码拆分技术综合指南

经过 Aleksandr Guzenko11m2023/04/24
Read on Terminal Reader

太長; 讀書

微前端 (MF) 可用于组织大型项目的开发团队。 MF 更像是关于如何管理大型项目中的开发复杂性的**组织决策**。 MFs 不会帮助你加速前端,一些实现,相反,甚至会减慢它。
featured image - 打破单体:代码拆分技术综合指南
Aleksandr Guzenko HackerNoon profile picture


过去几年,前端社区一直在积极讨论和使用“微前端”(以下简称MF)这个词。不同的公司分享了他们组织此类 架构解决方案的方法,但是到目前为止,对于 MF 旨在在 Web 上解决的问题、它们的适用性标准以及使用中的局限性的描述很少。


在本文中,我尝试比较组织 MF 的不同方法,并就在何处使用哪种方法提出建议。


这篇文章对于分析师和开发团队在设计项目架构和布局流程时很有用,对于产品所有者也很有用,因为 MF 的引入可以提供更易于管理的开发。

微前端方法:它是什么以及为什么需要它?

在我们继续MF的定义之前,让我们看一下项目中可能遇到的几个问题:


  1. 你有一个大项目。项目的大小通常是主观的,可以根据功能的数量和开发人员的数量来凭经验确定。如果你有足够的工作来迷惑 1-2 个前端,同时他们不会“推肘子”——这是一个小项目,3-6 是中型,超过 6-8 已经是大项目.


  2. 你有一个大团队。同样,根据经验,这是 10 多个前端,其余参与者不算数。通常,这种规模的团队已经可以划分为承担特定支持功能的子团队,并拥有自己的分析师、后端人员和 QA。


  3. 你有很好的功能。单个开发人员只能为其子团队维护一段代码。由于对主题领域的无知或实施第三方逻辑的复杂性,改进其余代码的成本可能很高。


问题可能会进一步恶化:

  • 改变堆栈的愿望;

  • 公司要求支持一系列相关项目。


您如何组织这么多人之间的关系?您如何在这种规模的项目上构建流程?您如何正确划分责任范围?收集到的问题越多,就越值得考虑引入微正面的方法。因为这是代码和项目团队分解的发展趋势的自然延续。





因此,MF 方法是将整体前端划分为单独的代码库,这些代码库存储在单独的存储库中,单独的子团队可以访问这些代码库。同时,他们可以/应该有自己的演示台、测试和发布周期。因此,微前端是界面的可拆卸部分。没有必要按页面划分,功能可以是端到端的(例如,企业 ui-kit)。


另外,值得强调的是,MF 更多是关于如何管理大型项目中的开发复杂性的组织决策。 MFs 不会帮助你加速前端,一些实现,相反,甚至会减慢它。但由于职责区域的分配和隔离测试,这种方法将加快开发本身。





微前端与延迟加载

相反,与 MF 相比,值得一提的是Lazy loading 。它们解决不同的问题,但有时人们认为这都是关于一件事的,因为在这两种情况下我们都“拆分”了应用程序。


延迟加载解决了性能问题:如何不强迫用户加载整个前端包,如何不等待超过必要的时间,以及如何更快地在客户端启动前端并更快地开始与之交互。


MF 不能解决性能问题,有时甚至会加剧它。但它们有助于以对特定子团队更舒适的方式组织开发,从而最大限度地减少上述问题。

构建时与运行时

现在让我们谈谈将 MF 组合到单个应用程序中的方法。无论您选择什么,它都应该看起来像一个单一的应用程序给用户。您可以在组装阶段和动态合并 - 在用户端执行代码期间。


因此,所有组织 MF 的方式都可以归因于构建时间或运行时间。每个都有其优点和缺点。


构建时间

运行

类型检查

+

-

版本控制

+

没有意义

独立部署

-

+


类型检查在现代开发中扮演着重要的角色。当它由单独的独立子团队运行时,它就成为必需品。如何确保 MF 的一致性,它们以正确的格式准确使用和传递数据等。


通过在构建时合并微前端,您不会被剥夺检查类型的机会。在运行时联合的情况下,您将必须编写集成测试,以便前端不会突然在生产中“爆炸”。


版本控制和独立部署是很矛盾的:


  • 版本控制意味着您可以使用其他团队的 MF 的任何版本。当您需要执行额外的工作来从其他人升级 MF-a 的依赖项时,尤其如此。每个团队选择一个更好的升级时间。


  • 独立部署给团队更多的自主权和独立性。始终使用最新版本的 MF 很重要。这需要向后兼容。


版本控制也可以通过运行时合并来实现,但这并不实用。为了独立部署才联系运行时才有意义,后者不能与版本控制共存。


接下来,我们将看到组合 MF 的每种方法的具体实现示例。

组织微前端的方法

框架

最古老的组织 MF 的方式。 iframe 是一个特殊的标签,用于传递将在主站点上显示的资源的地址。结果是站点中的站点。




在这些优势中,值得注意的是易于实施、逻辑和样式完全隔离、能够进行独立部署以及无需绑定到框架。


这些好处是以性能为代价的,因为每次插入 iframe 都会导致资源加载。您无法避免这种情况:尝试调试和重新附加 DOM 节点不会保存以前加载的资源,您必须重新下载它们。您可以通过配置缓存来最大限度地减少性能下降。


为此,您需要为不可变资源配置基于时间的缓存失效。幸运的是,所有用于收集的 js 和 css 文件的开箱即用的现代 cli 都会在名称上附加一个小哈希值。这种方法的缺点包括搜索机器人无法为后续索引呈现 iframe。


优点

缺点

易于实施

表现

逻辑和风格隔离

搜索引擎优化

独立部署


框架无关



Web组件

前端社区长期以来一直在等待原生组件的创建,但最终,它们从未像许多人预期的那样获得大众欢迎。三个最流行的前端框架(React、Vue、Angular)仍然以自己的方式创建组件。


尽管能够在 Web 组件上创建 MF,但我还没有在实践中看到过。这绝非偶然,有许多阻碍因素:


  • 无论是 libs Lit 还是 Stencil 都不够流行,不够普遍。此外,市场上没有足够的专家知道如何与他们合作或准备学习。


  • Angular 元素或 vue-custom-element 仍然很奇特。在本地环境中,使用它们没有多大意义。如果您已经拆分了应用程序,那么将其拆分为普通的 npm 包,以便稍后您可以根据需要连接组件。将 Web 组件与其他框架一起使用是不合理的,因为连同生成的组件,您需要连接编写它们的框架的迷你版本。


  • 将复杂的功能块转移到 Web 组件中可能代价高昂。由于您需要配置组件与应用程序其余部分的通信,因此在单独的自定义组件中取出整个页面可能是不合理的。


  • 搜索机器人无法创建网页组件,这会影响 SEO 优化。

优点

缺点

适用于横切功能

实施困难

兼容任何框架

搜索引擎优化

逻辑和风格隔离



NPM

使用npm 包进行开发有很多好处。开发人员只需从库中导入他们需要的组件和文件。同时,项目中保留了类型,有版本控制。构建是最优的:tree-shaking 有效(在构建过程中删除未使用的代码),开发人员可以轻松设置延迟加载。



然而,这种方法也有其缺点。在这种情况下,您将被迫维护堆栈的统一性,以及维护 MF 及其传递依赖项的版本。另一方面,这可能是一个加号:通过发布新版本的包,其他团队可以在他们需要引入额外功能时,在方便的时候承担提升依赖项版本的任务,或者相反, 删除一些东西。


优点

缺点

表现

没有独立部署

搜索引擎优化

单栈

类型检查


版本控制



git 子模块(或另一种制作像 Lerna 的 monorepos 的方法)

作为 npm 包上 MF 的替代方案,请考虑 git 子模块。实际上,这些是存储库中的存储库,其中也可以有存储库。您可以在子模块中设置不同的分支。例如,特性模块可以有一个什么都没有的虚拟分支。这是必要的,以便装配速度更快并且其他模块不会产生副作用。虚拟分支对于本地开发和测试来说非常方便。







就其优点和缺点而言,这种方法非常接近 npm 包。我们也只是导入一些东西,但是是从邻近的文件夹中,而不是从包中,然后使用它。


我们来看看这两种方式的区别:


  • 事实上,NPM 包是一个有限的、适度隔离的微型产品,有自己的发布和版本控制。一切都专注于创建可重用的功能。但是应用程序可能会很复杂/令人费解并且很臭,所以打包一个大型单体应用程序的成本可能会很高。这是考虑子模块的合理之处,因为当我们将文件夹移动到单独的存储库时,它们允许您非常粗略地切割存储库,而无需任何额外准备。


  • NPM 包可以递归嵌套。子模块也是如此,但在程序集级别,如果其中一个子模块作为单独的子模块多次包含在不同的文件夹中,它们可能会开始复制功能。在这种情况下,值得使用更扁平的模块结构。


  • 如果您需要一次在所有包中快速推出一个功能,那么跨包开发可能会非常不方便。虽然子模块上的一切都保持不变,但您可以进行影响其他子模块的编辑。这样的话,就很容易调试了。但最终,您不会自己合并更改 - 在合并请求级别,您接触其模块的第三方团队可能会要求您使代码符合他们的规则。


npm

git 子模块

可重用性

粗略的功能切割

任意嵌套的依赖

平面结构

跨平台开发

使用任何模块进行开发


单温泉

single-spa 本质上是一个结合了其他框架的框架。隐藏了大量细微差别的令人难以置信的强大技术是另一篇文章的主题。

该方案类似于 iframe,但 MF 的加载现在通过原生 import + importmap 完成,或者如果需要 polyfill,则通过 Systemjs 完成。





与所有组织 MF 的方法不同,这个方法是高度定制的,可以在其自身下组合不同的框架。但值得注意的是,不要为了技术本身而使用技术。如果可以通过一个堆栈来完成,则需要使用它。开发可能永远背负着维护技术上复杂的项目和修复不同应用程序副作用的任何错误的负担。用户可能会感到不适,因为。下载到客户端的代码量将增加(不同功能的不同框架的核心+ single-spa 本身及其插件的核心)。

优点

缺点

独立部署

大量文档,仍未涵盖所有情况

框架无关

SEO 的困难

强大的命令行界面



Webpack 5 模块联盟

专门为创建 MF 而开发的 webpack 5 插件。有前途的技术:一个小型构建插件,用于在运行时进行更正确的捆绑和动态导入。


该方案几乎一对一重复single-spa,但现在使用动态导入来加载MF



优点

缺点

独立部署

低级

容易实现


兼容SSR




如何选择在您的情况下使用什么?

让我们来看看可以应用什么以及用于什么:


  • iframe - 用于组合不协调的单个插入

  • 网络组件——当你需要一个小的端到端功能而不被框架所束缚时,比如企业 ui-kit

  • npm 包- 如果项目之间存在可重用性和/或您需要在构建时进行类型检查

  • git submodules - 当你需要粗略地分割一个项目并分配责任区域时

  • single-spa - 当强烈需要无限期地组合多个框架时,最好没有 SSR

  • module-federation - 使用 MF 的所有其他场景,受制于堆栈的统一性。


每种方法都有自己的好处,一切都应该有它的位置。在切换到 MF 之前,我们建议您考虑一下您是否真的需要它。无论选择哪种方法,都不可避免地会使开发、CI/CD 或性能层面的事情复杂化。如果有机会留在单一堆栈和单体应用程序上,请欣然接受这个机会。


当然,不要忘记用户。最终,他们下载所有连接的框架,并忍受由于 MF 在不同功能部分的不正确集成而可能出现的错误。反过来,企业将不得不为所有这些的实施和支持付出代价。