Monorepos前端代码指南

本文概述

  • 工具审查
  • 总结
Monorepos是讨论的热门话题。最近, 有很多文章介绍了为什么和不应该在项目中使用这种类型的体系结构, 但是其中大多数都以一种或另一种方式存在偏见。本系列旨在收集和解释尽可能多的信息, 以了解如何以及何时使用monorepos。
Monorepository是一个建筑概念, 基本上包含其标题中的所有含义。无需管理多个存储库, 而是将所有隔离的代码部分保留在一个存储库中。请记住” 隔离” 一词, 这意味着monorepo与单体应用程序没有任何共同之处。你可以在一个存储库中保留多种逻辑应用程序;例如, 一个网站及其iOS应用。
Monorepos前端代码指南

文章图片
这个概念相对古老, 大约十年前出现。 Google是最早采用这种方法来管理其代码库的公司之一。你可能会问, 如果它已经存在了十年, 那么为什么仅到现在它才是一个热门话题?通常, 在过去的5-6年中, 许多事情发生了戏剧性的变化。 ES6, SCSS预处理器, 任务管理器, npm等-如今, 要维护一个基于React的小型应用程序, 你必须处理项目捆绑程序, 测试套件, CI / CD脚本, Docker配置, 以及谁知道什么。现在想象一下, 你需要维护一个包含许多功能区域的庞大平台, 而不是一个小型应用程序。如果你在考虑体系结构, 则将需要做两件事:分开关注并避免代码重复。
为此, 你可能需要将大型功能隔离到某些程序包中, 然后通过主应用程序中的单个入口点使用它们。但是, 你如何管理这些软件包?每个程序包都必须具有自己的工作流环境配置, 这意味着每次要创建新程序包时, 都必须配置一个新环境, 复制所有配置文件, 依此类推。或者, 例如, 如果你必须在构建系统中进行某些更改, 则必须遍历每个存储库, 进行提交, 创建请求请求, 然后等待每个构建, 这会使你的速度大大降低。在这一步, 我们正在满足monorepos。
与其拥有大量具有自己配置的存储库, 不如说只有一个事实来源— monorepo:一个测试套件运行程序, 一个Docker配置文件和一个用于Webpack的配置。而且, 你仍然具有可伸缩性, 分离关注点的机会, 与常见软件包的代码共享以及许多其他专家。听起来不错吧?好吧, 是的。但是也有一些缺点。让我们仔细看看在野外使用monorepo的确切利弊。
Monorepo的优势:
  • 一处存储所有配置和测试的地方。由于所有内容都位于一个存储库中, 因此你可以一次配置CI / CD和捆绑程序, 然后只需重新使用配置来构建所有软件包, 然后再将其发布到远程。单元测试, 端到端测试和集成测试也是如此—你的CI将能够启动所有测试, 而无需处理其他配置。
  • 使用原子提交轻松重构全局功能。无需为每个存储库执行拉取请求, 而是弄清楚以什么顺序构建更改, 你只需要发出一个原子性拉取请求, 其中将包含与你要使用的功能有关的所有提交。
  • 简化程序包发布。如果计划在依赖于另一个具有共享代码的软件包的软件包中实现新功能, 则可以使用单个命令来实现。此功能需要一些其他配置, 稍后将在本文的工具评论部分中进行讨论。当前, 有很多工具可供选择, 包括Lerna, Yarn Workspaces和Bazel。
  • 依赖管理更容易。只有一个package.json。每当你要更新依赖项时, 都无需在每个存储库中重新安装依赖项。
  • 与共享程序包一起重用代码, 同时保持隔离状态。 Monorepo使你可以重用其他软件包中的软件包, 同时使它们彼此隔离。你可以使用对远程包的引用, 并通过单个入口点使用它们。要使用本地版本, 你可以使用本地符号链接。可以通过bash脚本或通过引入一些其他工具(如Lerna或Yarn)来实现此功能。
Monorepo的缺点:
  • 无法将访问限制为仅对应用程序的某些部分进行限制。不幸的是, 你不能只共享monorepo的一部分-你将不得不授予对整个代码库的访问权限, 这可能会导致某些安全问题。
  • 在大型项目上工作时, Git性能不佳。这个问题仅在提交超过一百万次的大型应用程序中开始出现, 并且每天有数百名开发人员在同一个仓库中同时进行工作。当Git使用有向无环图(DAG)表示项目历史时, 这尤其麻烦。随着大量的提交, 随着历史的加深, 遍历图形的任何命令都可能变得缓慢。由于引用数量(即分支或标签, 可以通过删除不再需要的引用解决), 并且跟踪的文件数量(以及它们的重量, 即使可以使用来解决繁重的文件问题), 性能也会降低Git LFS)。
    【Monorepos前端代码指南】注意:如今, Facebook试图通过修补Mercurial来解决VCS可伸缩性问题, 而且可能很快就不会成为一个大问题。
  • 构建时间更长。因为你将在一个地方放置很多源代码, 所以CI花费所有时间来运行所有内容以批准每个PR。
工具审查 用于管理单色仓库的工具集在不断增长, 目前, 很容易迷失在单色仓库的所有各种构建系统中。通过使用此存储库, 你可以始终了解流行的解决方案。现在, 让我们快速浏览一下当今JavaScript中大量使用的工具:
  • Bazel是Google面向单一仓库的构建系统。有关Bazel的更多信息:awesome-bazel
  • Yarn是一个JavaScript依赖项管理工具, 它通过工作区支持monorepos。
  • Lerna是用于管理基于Yarn的具有多个软件包的JavaScript项目的工具。
大多数工具使用的方法非常相似, 但是有一些细微差别。
Monorepos前端代码指南

文章图片
由于这是一个相当大的主题, 因此我们将在Lerna工作流以及本文第2部分中的其他工具中进行更深入的研究。现在, 让我们仅了解其中的内容:
勒拿湖
这个工具在处理语义版本, 设置构建工作流, 推送软件包等方面确实有帮助。Lerna背后的主要思想是你的项目有一个packages文件夹, 其中包含所有隔离的代码部分。除软件包外, 你还有一个主应用程序, 例如, 它可以存在于src文件夹中。 Lerna中的几乎所有操作都是通过一条简单的规则进行的-你遍历所有软件包, 并对它们进行一些操作, 例如, 增加软件包版本, 更新所有软件包的依赖性, 构建所有软件包等。
使用Lerna, 你可以使用两种方式选择如何使用软件包:
  1. 无需将它们推送到远程(NPM)
  2. 将包裹推送到远程
使用第一种方法时, 你可以为包使用本地引用, 并且基本上不关心使用符号链接来解决它们。
但是, 如果你使用第二种方法, 则必须从远程导入软件包。 (例如, 从@ yourcompanyname / packagename; 导入{something}), 这意味着你将始终获得软件包的远程版本。对于本地开发, 你将必须在文件夹的根目录中创建符号链接, 以使捆绑程序能够解析本地软件包, 而不是使用node_modules /中的软件包。因此, 在启动Webpack或你喜欢的捆绑程序之前, 你必须启动lerna bootstrap, 该程序会自动链接所有软件包。
Monorepos前端代码指南

文章图片

Yarn最初是NPM软件包的依赖项管理器, 最初并不是为支持Monorepos而构建的。但是在1.0版中, Yarn开发人员发布了一项名为” 工作区” 的功能。在发行时, 它还不稳定, 但是一段时间后, 它就可以用于生产项目。
工作区基本上是一个程序包, 它具有自己的package.json, 并且可以具有一些特定的生成规则(例如, 如果在项目中使用TypeScript, 则为单独的tsconfig.json)。实际上, 你可以使用bash在不使用Yarn Workspaces的情况下以某种方式进行管理, 并且具有完全相同的设置, 但是此工具有助于简化安装和更新每个程序包的依赖项的过程。
乍一看, Yarn及其工作区提供了以下有用的功能:
  1. 所有软件包的根目录中的单个node_modules文件夹。例如, 如果你具有packages / package_a和packages / package_b, 以及它们自己的package.json, 则所有依赖项都将仅安装在根目录中。这是Yarn和Lerna工作方式之间的区别之一。
  2. 依赖符号链接允许本地程序包开发。
  3. 适用于所有依赖项的单个锁定文件。
  4. 如果你只想为一个软件包重新安装依赖项, 则集中更新依赖项。可以使用-focus标志来完成。
  5. 与Lerna集成。你可以轻松地让Yarn处理所有安装/符号链接, 并让Lerna负责发布和版本控制。到目前为止, 这是最受欢迎的设置, 因为它需要的工作量少并且易于使用。
有用的链接:
  • 纱线工作区
  • 如何构建TypeScript单仓库项目
巴塞尔
Bazel是用于大型应用程序的构建工具, 它可以处理多语言依赖性并支持许多现代语言(Java, JS, Go, C ++等)。在大多数情况下, 将Bazel用于中小型JS应用程序是过大的, 但是在大规模情况下, 由于其性能, 它可能会带来很多好处。
从本质上讲, Bazel看起来类似于Make, Gradle, Maven和其他工具, 这些工具允许基于包含构建规则和项目依赖项描述的文件进行项目构建。 Bazel中的同一文件称为BUILD, 位于Bazel项目的工作空间内。 BUILD文件使用它的Starlark, 这是一种人类可读的高级构建语言, 看起来很像Python。
通常, 你对BUILD的处理不会太多, 因为可以在网上轻松找到很多样板文件, 这些样板文件已经配置好并且可以进行开发了。每当你要构建项目时, Bazel基本上都会执行以下操作:
  1. 加载与目标有关的BUILD文件。
  2. 分析输入及其依赖项, 应用指定的构建规则, 并生成操作图。
  3. 在输入上执行构建操作, 直到产生最终的构建输出。
有用的链接:
  • JavaScript和Bazel –用于从头开始为JS设置Bazel项目的文档。
  • Bazel的JavaScript和TypeScript规则-JS样板。
总结 Monorepos只是一个工具。关于它是否有未来存在很多争论, 但事实是, 在某些情况下, 此工具可以有效地完成工作并对其进行处理。在过去的几年中, 该工具不断发展, 获得了更大的灵活性, 克服了很多问题, 并删除了配置方面的复杂性层。
仍然有很多问题需要解决, 例如Git的性能不佳, 但是希望可以在不久的将来解决。
如果你想学习为你的应用程序构建健壮的CI / CD管道, 我建议你如何使用GitLab CI构建有效的初始部署管道。

    推荐阅读