前端项目代码编码规范(前端代码单一代码仓库指南)
前端项目代码编码规范(前端代码单一代码仓库指南)大多数工具都使用非常相似的方法,但存在一些细微差别。管理 monorepos 的工具集在不断增长,目前,很容易迷失在 monorepos 的各种构建系统中。通过使用此 repo,您始终可以了解流行的解决方案。但是现在,让我们快速浏览一下如今在 JavaScript 中大量使用的工具:与其拥有大量带有自己配置的存储库,我们将只有一个事实来源——monorepo:一个测试套件运行器、一个 Docker 配置文件和一个 Webpack 配置。而且您仍然具有可扩展性、分离关注点的机会、与通用包的代码共享以及许多其他优点。听起来不错,对吧?嗯,是的。但也有一些缺点。让我们仔细看看在世纪钟使用 monorepo 的确切利弊。Monorepo 优势:Monorepo 的缺点:
Monorepos 是讨论的热门话题。最近有很多关于为什么你应该和不应该在你的项目中使用这种类型的架构的文章,但其中大多数都以一种或另一种方式存在偏见。本系列试图收集和解释尽可能多的信息,以了解如何以及何时使用 Monorepos。
一个Monorepository是一个架构的概念,它基本上包含在其标题中包含的意思。您无需管理多个存储库,而是将所有隔离的代码部分保存在一个代码存储库中。请记住隔离这个词——这意味着 monorepo 与单体应用程序没有任何共同之处。您可以在一个代码仓中保存多个逻辑应用程序;例如,一个网站及其 iOS 应用程序。
这个概念比较古老,大约在十年前就出现了。 Google 是最早采用这种方法来管理其代码库的公司之一。你可能会问,如果它已经存在了十年,那为什么它现在才这么热门?大多数情况下,在过去的 5-6 年中,很多事情都发生了巨大的变化。 ES6、SCSS 预处理器、任务管理器、npm 等——如今,要维护一个基于 React 的小型应用程序,您必须处理项目捆绑器、测试套件、CI/CD 脚本、Docker 配置,以及谁知道还有什么。现在想象一下,你需要维护一个由很多功能区组成的巨大平台,而不是一个小应用程序。如果您正在考虑架构,您将需要做两件事:分离关注点并避免代码重复。
为了实现这一点,您可能希望将大型功能隔离到一些包中,然后通过主应用程序中的单个入口点使用它们。但是你如何管理这些包呢?每个包都必须有自己的工作流环境配置,这意味着每次要创建新包时,都必须配置新环境、复制所有配置文件等。或者,例如,如果您必须更改构建系统中的某些内容,则必须检查每个存储库、提交、创建拉取请求并等待每个构建,这会大大减慢您的速度。在这一步,我们遇到了 monorepos。
与其拥有大量带有自己配置的存储库,我们将只有一个事实来源——monorepo:一个测试套件运行器、一个 Docker 配置文件和一个 Webpack 配置。而且您仍然具有可扩展性、分离关注点的机会、与通用包的代码共享以及许多其他优点。听起来不错,对吧?嗯,是的。但也有一些缺点。让我们仔细看看在世纪钟使用 monorepo 的确切利弊。
Monorepo 优势:
- 一个存储所有配置和测试的地方。由于所有内容都位于一个 repo 中,因此您可以配置一次 CI/CD 和捆绑器,然后只需重新使用配置来构建所有包,然后再将它们发布到远程。单元、e2e 和集成测试也是如此——您的 CI 将能够启动所有测试,而无需处理额外的配置。
- 使用原子提交轻松重构全局功能。无需为每个 repo 执行拉取请求,找出构建更改的顺序,您只需要发出一个原子拉取请求,其中将包含与您正在使用的功能相关的所有提交。
- 简化的包发布。如果您计划在依赖于具有共享代码的另一个包的包中实现新功能,则可以使用单个命令来完成。这是一个需要一些额外配置的功能,稍后将在本文的工具回顾部分中讨论。目前,有丰富的工具可供选择,包括 Lerna、Yarn Workspaces 和 Bazel。
- 更容易地依赖管理。只有一个package.json。每当您想要更新依赖项时,都无需在每个 repo 中重新安装依赖项。
- 重用共享包的代码,同时保持它们的隔离。Monorepo 允许您重用其他包中的包,同时保持它们彼此隔离。您可以使用对远程包的引用并通过单个入口点使用它们。要使用本地版本,您可以使用本地符号链接。此功能可以通过 bash 脚本或通过引入一些其他工具(如 Lerna 或 Yarn)来实现。
Monorepo 的缺点:
- 无法仅限制对应用程序某些部分的访问。不幸的是,您不能只共享 monorepo 的一部分——您必须授予对整个代码库的访问权限,这可能会导致一些安全问题。
- 处理大型项目时 Git 性能不佳。这个问题开始只出现在具有超过一百万次提交和数百名开发人员每天在同一个 repo 上同时工作的大型应用程序上。这变得特别麻烦,因为 Git 使用有向无环图 (DAG) 来表示项目的历史。随着大量提交,随着历史的加深,任何遍历图表的命令都可能变得缓慢。由于引用的数量(即分支或标签,可以通过删除不再需要的引用来解决)和跟踪的文件数量(以及它们的重量,即使可以使用解决重文件问题),性能也会降低Git LFS)。注意:现在,Facebook 试图通过修补 Mercurial来解决 VCS 可扩展性问题,而且可能很快,这不会成为一个大问题。
- 更长的构建时间。因为您将在一个地方拥有大量源代码,所以您的 CI 需要更多时间来运行所有内容以批准每个 PR。
管理 monorepos 的工具集在不断增长,目前,很容易迷失在 monorepos 的各种构建系统中。通过使用此 repo,您始终可以了解流行的解决方案。但是现在,让我们快速浏览一下如今在 JavaScript 中大量使用的工具:
- Bazel是 Google 面向 monorepo 的构建系统。更多关于 Bazel:awesome-bazel
- Yarn是一个 JavaScript 依赖管理工具,通过工作区workspace支持 monorepos。
- Lerna是一个用于管理具有多个包的 JavaScript 项目的工具,它构建在 Yarn 上。
大多数工具都使用非常相似的方法,但存在一些细微差别。
我们将深入探讨 Lerna 工作流程以及本文第 2 部分中的其他工具,因为这是一个相当大的主题。现在,让我们大致了解一下里面的内容:
这个工具在处理语义版本、设置构建工作流、推送包等方面真的很有帮助。 Lerna 背后的主要思想是你的项目有一个包文件夹,其中包含所有孤立的代码部分。除了包之外,您还有一个主应用程序,例如可以位于 src 文件夹中。Lerna 中的几乎所有操作都通过一个简单的规则进行——您遍历所有包,并对它们执行一些操作,例如,增加包版本、更新所有包的依赖项、构建所有包等。
使用 Lerna,您有两种使用包的选择:
- 无需将它们推送到远程(NPM)
- 将您的包推送到远程
使用第一种方法时,您可以对包使用本地引用,并且基本上不关心符号链接来解决它们。
但是如果您使用第二种方法,您将被迫从远程导入您的包。(例如,import { something } from @yourcompanyname/packagename;),这意味着您将始终获得包的远程版本。对于本地开发,您必须在文件夹的根目录中创建符号链接,以使捆绑程序解析本地包,而不是使用node_modules/. 这就是为什么在启动 Webpack 或您最喜欢的打包器之前,您必须启动lerna bootstrap,它会自动链接所有包。
YarnYarn 最初是一个 NPM 包的依赖管理器,它最初并不是为了支持 monorepos 而构建的。但是在 1.0 版本中,Yarn 开发人员发布了一个名为Workspaces的功能。在发布时,它不是那么稳定,但一段时间后,它可以用于生产项目。
Workspace基本上是一个包,它有自己的package.json并且可以有一些特定的构建规则(例如,如果您在项目中使用 TypeScript,则单独的tsconfig.json。)。实际上,您可以使用 bash 以某种方式在没有 Yarn Workspaces 的情况下进行管理并具有完全相同的设置,但此工具有助于简化安装和更新每个包的依赖项的过程。
一目了然,Yarn 及其工作区提供了以下有用的功能:
- node_modules所有包的根目录中的单个文件夹。例如,如果您拥有packages/package_a和packages/package_b- 拥有自己的package.json- 所有依赖项将仅安装在根目录中。这是 Yarn 和 Lerna 工作方式之间的差异之一。
- 允许本地包开发的依赖符号链接。
- 所有依赖项的单个锁文件。
- 如果您只想为一个包重新安装依赖项,则集中依赖项更新。这可以使用-focus标志来完成。
- 与Lerna的整合。您可以轻松地让 Yarn 处理所有安装/符号链接,并让 Lerna 负责发布和版本控制。这是迄今为止最流行的设置,因为它需要更少的努力并且易于使用。
Bazel 是大型应用的构建工具,可以处理多语言依赖,支持很多现代语言(Java、JS、Go、C 等)。在大多数情况下,将 Bazel 用于中小型 JS 应用程序是矫枉过正的,但在大规模上,它可能会因为其性能而提供很多好处。
就其性质而言,Bazel 看起来类似于 Make、Gradle、Maven 和其他允许基于包含构建规则和项目依赖项描述的文件进行项目构建的工具。Bazel 中的同一个文件称为BUILD,位于 Bazel 项目的工作空间内。在BUILD文件使用其Starlark,这看起来很像Python的人类可读的,高层次的构建语言。
通常,您不会与BUILD打交道,因为可以在 Web 上轻松找到很多样板,并且已经配置好并准备好进行开发。每当你想构建你的项目时,Bazel 基本上会做以下事情:
- 加载与目标相关的BUILD文件。
- 分析输入及其依赖关系,应用指定的构建规则,并生成一个动作图。
- 对输入执行构建操作,直到生成最终构建输出。
Monorepos 只是一个工具。关于它是否有未来有很多争论,但事实是,在某些情况下,这个工具可以完成它的工作并以有效的方式处理它。在过去几年中,该工具不断发展,获得了更大的灵活性,克服了许多问题,并在配置方面消除了复杂性层。
还有很多问题需要解决,比如 Git 性能不佳,但希望这将在不久的将来得到解决。