为什么 Lerna 和 Yarn Workspaces 是构建单体仓库的完美组合:深入剖析其特性和性能
什么是单体仓库?它与多仓库有何区别?
单体仓库的工具格局
单体仓库配置的不同变体
lerna 和 yarn 工作区
时序比较
结论
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
这篇文章是我对单体仓库(Mono-Repo)的看法。在简要介绍单体仓库并将其与多仓库(Multi-Repo)进行比较之后,我将深入探讨用于搭建单体仓库的工具。
我不想详细评估哪种仓库类型在什么情况下更优。不过,本文的重点在于单体仓库(Mono-Repos)以及lerna、npm和yarn(工作区)如何提供帮助。将这些工具结合使用也很有意义。特别是lerna和yarn 工作区可以在同一个项目中和谐共存。如何做到呢?我们稍后会揭晓。
什么是单体仓库?它与多仓库有何区别?
像Lerna和Yarn Workspaces这样的工具的出现起到了决定性作用,使得在单个代码库中管理代码库(即Mono-Repo)在过去一两年里获得了广泛的认可。围绕这个主题,已经有很多文章发表,也有很多会议演讲。
简而言之,所谓的单体仓库( Mono-Repo)是指一个包含多个项目的(Git)仓库。这些项目被称为工作区或包。与之相反,使用多个仓库,每个仓库只包含一个项目,则称为多仓库(Multi-Repo)方法。当然,两种方法也可以结合使用。在我目前的工作中,我们组成了多个团队,每个团队都有自己的仓库。有些团队采用单体仓库方法,有些团队则坚持多仓库原则。此外,还有一些团队同时采用这两种方法,因为仓库中包含的技术也是决策时需要考虑的因素(例如,每个 Java 微服务都属于一个独立的 Git 仓库)。
要了解Mono-Repos和Multi-Repos的区别以及优缺点,我推荐阅读 Markus Oberlehner 关于Monorepos in the Wild的文章。
单体仓库的工具格局
单体仓库(Mono-Repo)托管一个或多个项目或包。这些包是“迷你仓库”(Mini-Repo),可以独立进行版本控制、构建和发布。因此,每个包都有自己的package.json文件,因为每个包本身就是一个完整的项目。
包之间可能存在依赖关系。这些依赖关系通过符号链接来管理。
正如我们稍后将看到的,Lerna和Yarn 工作区使我们能够在单个代码库中构建库和应用程序,而无需发布到 npm 或其他注册表。这些技术的精妙之处在于,它们可以通过分析每个项目根目录下的package.json文件来查找包依赖关系。因此,这些工具使得手动创建符号链接或直接使用“底层”的npm link 命令变得多余。
通过在本地共享组件,可以加快代码测试和调试周期。lerna和yarn workspaces结合使用,可以改善开发者在Mono-Repo中管理多个包的体验。
npm、yarn、yarn workspaces和lerna之间的相关性
我想阐明一下npm、yarn、yarn workspaces和lerna在Mono-repos主题中是如何相互关联的。请看下面的“集合图”。
它描绘了三个主要参与者及其相互关系。顺便说一句,不必过于纠结图中的比例。该图的目的只是为了展示事物之间的联系。
npm(标记为 1)和yarn(标记为 2)都是原生包管理器,它们有很多共同的特性(标记为 3)。例如,两者都利用package.json作为依赖管理容器,而 package.json 最初是由npm引入的。更多共同的概念和特性包括依赖管理、发布以及使用锁定文件来“冻结”依赖版本。甚至还有更多源自npm的特性也被yarn所利用,例如发布到 npm 注册表。
Yarn最初创建的原因之一是性能问题——使用npm在大型项目中安装依赖项耗时过长。另一个原因是 npm 缺少一些功能,例如完善的版本冻结机制、离线功能以及依赖项解析的确定性行为。不过,随着时间的推移, npm的许多缺陷已经得到弥补,如今两种技术在功能上都越来越兼容。
目前仍仅属于npm (1) 或yarn (2) 的文件分别是package-lock.json文件和yarn.lock文件。然而,对于我们这些应用开发者来说,lock 文件的不同实现方式其实并不重要。实际上,npm和yarn在版本管理的处理方式上几乎完全相同。
yarn的一项重要特性是yarn workspaces (4),它大约在一年前添加到yarn中。它通过原生Mono-Repo功能扩展了yarn 的功能。下一节将更详细地介绍Mono-Repo 的特性。
Mono-Repo——什么是原生代码?什么是用户空间?
请看下图,该图描述了 Mono-Repo 环境中的技术是如何相互连接的。
图中红色标记的技术提供了 Mono-Repo 功能。它们都基于npm或yarn 。除了npm link或yarn link之外,后者没有提供构建 Mono-Repo 的高级功能。
yarn workspaces是唯一原生支持 Mono-Repo 功能的代表。lerna已经存在很长时间了,甚至比yarn workspaces出现得更早。lerna借助npm或yarn等依赖管理工具,在用户层面提供 Mono-Repo 功能。
lerna利用语义链接来实现这一目标。它还支持使用yarn workspaces,并将 Mono-Repo 的管理完全交给yarn workspaces的原生功能。此外,lerna还提供完善的发布和版本管理功能,甚至可以独立发布各个项目。简而言之,lerna提供的功能远不止 Mono-Repo 管理。另一方面,yarn workspaces 的唯一目的就是简化 Mono-Repo 的工作流程。因此,您无需在两者之间做出选择。将 lerna与yarn workspaces结合使用是完全合理的。
Bolt是一个基于Yarn 工作区的新项目。它受Lerna的启发,旨在在此基础上添加更多实用命令。然而,由于我尚未成功在我的 Playground 项目中运行Bolt ,因此我没有任何使用经验。此外,我还注意到 Bolt 近期的提交次数相对较少。所以,本文不会深入探讨 Bolt。
单体仓库配置的不同变体
本节旨在快速概述不同工具的各种设置方法。您可以将屏幕截图视为一种“速查表”。重点在于不同方法的配置部分及其差异。
我创建了一个小型代码库来演示不同的变体。只需克隆演示项目代码库,然后切换分支即可查看不同的变体。README.md文件描述了如何引导和使用(即构建并运行示例应用程序)特定变体。本节和演示项目的另一个目标是提供一个简单的实验环境,以便从不同角度观察不同变体的实际运行情况:需要哪些配置步骤、构建和使用子项目(即包)需要哪些步骤、依赖管理如何工作,以及引导过程的时间安排有何影响。
1. 自己动手
我跳过了这部分,但你可以看看1-do-it-yourself分支。基本上,你需要使用npm link,创建语义链接并手动安装所有子项目。你应该能想象到,在实际项目中,这种做法有多么繁琐和不切实际。
2. 使用 npm 进行 lerna 测试
为了支持自动化方法一中此类手动任务,引入了lerna 。您需要在根文件夹中放置一个lerna.json文件。按照惯例,lerna默认使用npm 。
正如您在下一张截图中看到的,要让lerna正常运行,您基本上需要编辑两个文件: lerna.json和package.json。在lerna.json 文件中,您需要指定lerna在哪里查找软件包。
要启动所有子项目,您需要通过调用以下 npm 脚本来执行lerna bootstrap :
$ npm run bootstrap
这条命令的基本作用是进入所有包的根目录并执行`npm install`。查看这三个包,你会发现lerna导致 npm为每个包都创建了一个 ` node_modules`文件夹。
3. 用纱线
这与方法 2 的设置相同。唯一的区别在于,您需要在lerna.json文件中使用“npmClient”属性指定yarn作为客户端。引导过程也由lerna执行。
与方法一相比,区别是什么?几乎没有区别。主要区别在于个人喜好,因为唯一的区别在于Lerna使用npm还是yarn作为依赖管理器。至于选择哪种方法,答案可以归结为以下几个问题:
- 我更喜欢哪种语法?npm run <命令>还是yarn <命令>
- 我应该坚持这种准标准,还是更欣赏 Facebook 的努力?
- 我真的在意启动时间吗?如果是的话,请看下一章,其中提供了一些性能基准测试。
4. 纱线工作区
对于这种方法,您不需要lerna。yarn workspaces内置了Mono-Repo功能。要使用yarn workspaces,您需要yarn版本 1.0 或更高版本。如下面的屏幕截图所示,您不需要专门的配置文件。根文件夹中的package.json文件必须是私有的,并且必须包含一个“workspaces”属性,告诉yarn在哪里可以找到子项目(或yarn术语中的 workspaces)。
要启动包含所有工作区的项目,只需使用yarn即可,因为yarn workspaces本身就提供了此功能:
$ yarn install
或简称:
$ yarn
这结合了方法 1 和方法 2 的两个步骤:安装根文件夹的依赖项和引导所有软件包的依赖项。
与方法 1 和方法 2 相比,一个显著的区别是yarn workspaces只创建一个node_modules文件夹。所有依赖项都会被提升到根文件夹。备注:同时,使用lerna(不使用yarn workspaces)也可以通过`--hoist`标志实现这种行为。
5. 带纱线工作区的 lerna
要使用yarn workspaces配置lerna ,您需要在根目录的package.json 文件中进行与方法 4 中相同的配置。但是,您还需要在根目录下提供一个lerna.json文件。在该文件中,您需要告诉lerna使用yarn workspaces 。遗憾的是,您需要在lerna.json中重复指定子项目的位置。要引导项目,无需使用lerna bootstrap,只需按照方法 4 中的说明使用yarn install即可。调用lerna bootstrap意义不大,因为它实际上只是调用了yarn install本身。
在这种配置下,Lerna将依赖项和引导流程完全交给Yarn 工作区处理。因此,你需要进行更多配置才能实现与之前方法相同的效果。那么,为什么你应该选择这种方法而不是方法 4 呢?仔细想想——同时使用Lerna和Yarn 工作区是完全合理的。它们可以在Mono-Repo项目中和谐共存。
在这种情况下:
- Mono-Repo工作流程仅使用yarn workspaces。
- 您可以使用lerna的实用命令来优化多个包的管理,例如,选择性地执行npm脚本进行测试。
- 您使用lerna发布软件包,因为lerna的 version 和 publish 命令提供了复杂的功能。
lerna 和 yarn 工作区
上一节简要介绍了如何使用不同的配置搭建MonoRepos。本节则更侧重于lerna和yarn workspaces的特性。
yarn 工作区
目前,yarn workspaces是唯一原生支持Mono-Repos的技术。与lerna不同,使用 yarn 无需单独执行步骤来引导包的依赖项。yarn install会自动安装根文件夹的依赖项,然后再安装每个包的依赖项。
与Lerna不同,Yarn Workspace除了多项目配置的依赖管理之外,没有其他额外功能。由于它基于Yarn ,因此您可以使用Yarn的所有功能。
为了使用yarn workspaces,Facebook 引入了一些额外的命令,这些命令只有在Mono-Repos的上下文中才有意义。
以下命令将显示当前项目的工作区依赖关系树:
$ yarn workspaces info
下一张收据允许您在选定的工作区(即包)中运行选定的yarn命令:
$ yarn workspace <package-name> <command>
例如,使用以下命令,react将被添加到名为“awesome-package”的包/工作区中作为开发依赖项(您也可以使用-D代替--dev):
$ yarn workspace awesome-package add react --dev
接下来是一个从特定软件包中移除依赖项的示例:
$ yarn workspace web-project remove some-package --save
如果要为所有包添加通用依赖项,请进入项目根文件夹并使用-W(或--ignore-workspace-root-check)标志:
$ yarn add some-package -W
否则, yarn会报错。
我使用以下命令将我自己的一个包(“awesome-components”)作为依赖项添加到另一个包(“awesome-app”)中。我发现添加本地包时需要指定版本号,否则yarn会尝试在注册表中查找依赖项。
$ yarn workspace @doppelmutzi/awesome-app add @doppelmutzi/awesome-components@0.1.0 -D
利用工作区功能,yarn不会将依赖项添加到任何包的node_modules目录中,而只会添加到根目录,也就是说,yarn会将所有依赖项提升到根目录。yarn利用符号链接指向不同的包。因此,yarn在项目中只会包含一次依赖项。
要在Mono-Repo环境中使用原本不兼容的第三方依赖项,您必须使用yarn workspaces的noHoist功能。您需要在项目根目录的package.json 文件中指定此功能,如下例所示。
// package.json
{
...
"workspaces": {
"packages": ["packages/*"],
"nohoist": [
"**/react-native"
]
}
...
}
欲了解更多信息,请查看ConnectDotz的演示项目。
勒纳
与Yarn 工作区类似,Lerna为前端项目添加了MonoRep功能。但是,如上所述,Lerna运行在“用户空间”,无法原生添加此类功能。
如果将Lerna配置为使用Yarn Workspaces,则Lerna会将所有依赖管理工作交给Yarn Workspaces。如果将Lerna配置为使用npm或Yarn,则Lerna会利用符号链接自行提供Mono-Repo功能。在这种情况下,您需要使用Lerna Bootstrap来初始化所有包的依赖项。
John Tucker 写了一篇很棒的文章,介绍了如何使用lerna的命令来初始化项目和管理依赖项。
要将React作为依赖项安装到所有包中,可以使用以下命令:
$ lerna add react
如果只想将React作为依赖项安装到特定软件包中,请执行以下命令:
$ lerna add react --scope my-package
如果您已经为每个软件包安装了React,但只想针对特定软件包升级/降级到特定版本,则可以这样做:
$ lerna add react@16.0.0 --scope my-package
lerna带有一些标志。它们构成了lerna子命令的选项,用于进行过滤。
考虑以下名为“test”的 npm 脚本。下面的两个 shell 命令展示了如何使用`--scope`标志和通配符仅对特定包执行测试。`lerna`会尝试对每个匹配的包执行`yarn test` 。
// package.json
{
...
"scripts": {
"test": "lerna exec yarn test“
}
...
}
$ yarn test --scope @my-company-services/*
$ yarn test --scope @my-company/web-*
根据文档,lerna也像yarn workspaces的默认行为一样,将共享依赖项提升到根文件夹。因此,您需要使用`--hoist`标志。
$ lerna add react -D --hoist
如果你使用lerna,就会面临一个问题:选择npm还是yarn?正如你在上一节的“速查表”中看到的,你可以根据自己的喜好轻松地在不同的包管理器之间切换。
高级前端工作流程功能和命令
即使你选择使用Yarn Workspaces进行依赖管理,最好也同时使用Lerna。原因在于Lerna提供了一系列实用命令来优化多包管理。例如,只需一条Lerna命令,你就可以遍历所有或特定包,并对每个包执行一系列操作(例如代码检查、测试和构建)。因此,它与Yarn Workspaces相辅相成,后者负责处理依赖管理流程。
在根目录下使用Lerna进行测试或代码检查比从每个包文件夹手动调用所有操作要快得多。John Tucker 的博客文章详细介绍了如何使用Lerna进行测试。
版本控制和发布是重要的开发主题,而Lerna在这方面也表现出色。Lerna允许您使用两种版本控制模式:
-
固定/锁定模式:所有软件包的版本都可以在单个位置(lerna.json文件)进行管理。如果某个软件包自上次发布以来进行了更新,它将被更新到新版本。因此,任何软件包的重大更改都会导致所有软件包的主版本号发生变化。
-
独立模式:软件包版本可以彼此独立地递增。因此,lerna.json文件中的“version”键需要设置为“independent”。这种方法提供了更大的灵活性,对于组件松耦合的项目尤其有用。
您可以发布自上次发布以来已更改的软件包:
$ lerna publish
在独立模式下,使用 publish 命令进行版本更新有多种选择。除了使用semver关键字外,还可以使用以下版本更新标志之一:from-git或from-package。
以下命令使用传统的提交标准将代码发布到 npm 注册表。
$ lerna publish --conventional-commits --yes
上述命令还会生成变更日志文件。根据Lerna 的文档,存在不同的变更日志预设,例如Angular或Bitbucket格式。另外,使用`--yes` 标志会跳过所有确认提示。
在lerna.json文件中,您可以全局定义必须使用常规提交方式,而无需使用标志:
// lerna.json
...
"command": {
"publish": {
"conventionalCommits": true,
"yes": true
}
}
...
@jsilvax解释了lerna 的常规提交是如何工作的,以及如何使用commitlint来强制执行。
由于版本控制和发布是复杂的主题,以上部分仅展示了lerna功能的一小部分示例。我不会深入探讨更多细节,因为这会超出本文的范围。
时序比较
人们坚持使用Yarn而不是npm 的主要原因之一是 Yarn 在安装依赖项方面的性能优势。最初,Yarn的开发就是为了解决npm安装依赖项速度过慢的问题(此外, npm还缺少一些重要功能)。而npm目前已经发布了 6 版本,并投入了大量精力来弥补这一差距。
由于实现MonoRepo 的方法多种多样,我们来看看这些不同方法的性能表现。在本节的剩余部分,我将展示我的性能实验结果。我克隆了Babel 项目(大约在 2018 年 10 月),因为它代表了一个包含大量包(准确来说是 142 个)的真实MonoRepo 。有趣的是, Babel的原始配置使用了lerna,其配置将yarn指定为npmClient(没有yarn workspaces),并禁用了yarn的 lock 文件生成功能。
对于每种方法(2 – 5),我都执行以下操作:
- 我更改了相应方法所需的配置(即,根据需要调整package.json和lerna.json )。
- 我测量了安装依赖项和执行专用引导步骤(如果需要)所花费的时间。
- 我测量了三种不同使用场景下的时间。每种使用场景我都进行了三次测量。
上述用例(UC)包括:
1) 我清空了 npm 或 yarn 缓存,删除了所有node_modules文件夹,并删除了所有package-lock.json或yarn.lock文件。2
) 缓存存在,我删除了所有node_modules文件夹,并删除了所有package-lock.json或yarn.lock文件。3
) 缓存存在,package-lock.json或yarn.lock文件也存在,我删除了所有node_modules文件夹。
为了清除缓存,我根据所使用的npm客户端执行了以下命令之一:
$ npm cache clean --force
或者
$ yarn cache clean
为了帮助删除锁定文件和node_modules文件夹,我在 Babel 的根文件夹中添加了一个名为cleanup.sh的脚本:
find . -type f -name 'yarn.lock' -exec rm {} +
find . -type f -name 'package-lock.json' -exec rm {} +
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
根据具体使用情况,我最终注释掉了前两行代码。
为了测量安装和引导依赖项步骤的执行时间,我使用了gnomon。以下命令示例展示了方法 2(使用lerna和npm)和 UC 1(空缓存、无node_modules文件夹、无锁定文件作为前提条件)的运行时间测量方法:
$ npm cache clean --force && ./cleanup.sh && npm i | gnomon && npm run bootstrap | gnomon
下面列出了各项测量结果。这些测量是随着时间的推移进行的,因此我尝试了不同的Node、npm、Yarn和Lerna版本,以了解不同版本是否会对性能产生不同的影响。
为了切换Node和npm版本,我使用了nvm。以下示例首先安装并使用Node v9 ,然后安装npm v5.7.1。
$ nvm install v9
$ nvm use v9
$ npm i -g npm@5.7.1
方法二(lerna 与 npm)– Node v10.12.0 / npm v6.4.1 / lerna 2.11.0
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 39.1680秒 | 64.7168秒 | 103.8848秒 |
| 1 | 40.8052秒 | 78.0730秒 | 118.8782秒 |
| 1 | 39.8729秒 | 64.0626秒 | 103.9355秒 |
| 2 | 23.9931秒 | 34.8695秒 | 58.8626秒 |
| 2 | 23.8788秒 | 38.7979秒 | 62.6767秒 |
| 2 | 25.4764秒 | 37.5166秒 | 62.993秒 |
| 3 | 16.7291秒 | 35.8081秒 | 52.5372秒 |
| 3 | 29.4270秒 | 72.3721秒 | 101.7991秒 |
| 3 | 39.4265秒 | 85.0043秒 | 124.4308秒 |
备注:说实话,我不知道为什么最后两篇日志的偏差这么大——也许是我的Macbook的工作负载太高了?!
方法二(lerna 与 npm)– Node v9.10.0 / npm v5.6.0 / lerna 2.11.0
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 38.1641秒 | 52.7642秒 | 90.9283秒 |
| 1 | 33.3413秒 | 57.4676秒 | 90.8089秒 |
| 1 | 32.3160秒 | 52.4869秒 | 84.8029秒 |
| 2 | 24.3268秒 | 41.6709秒 | 65.9977秒 |
| 2 | 26.4843秒 | 41.6038秒 | 68.0881秒 |
| 2 | 29.8368秒 | 43.3759秒 | 73.2127秒 |
| 3 | 18.2647秒 | 33.7095秒 | 51.9742秒 |
| 3 | 15.2864秒 | 33.4166秒 | 48.7030秒 |
| 3 | 15.9295秒 | 34.6834秒 | 50.6129秒 |
方法 3(带有纱线的 lerna) – Node v10.12.0/yarn 1.10.1/lerna 2.11.0
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 36.5181秒 | 58.5693秒 | 95.0874秒 |
| 1 | 29.9026秒 | 53.8042秒 | 83.7068秒 |
| 1 | 30.8910秒 | 60.2566秒 | 91.1476秒 |
| 2 | 15.6954秒 | 34.9247秒 | 50.6201秒 |
| 2 | 24.4038秒 | 36.8669秒 | 61.2707秒 |
| 2 | 16.1917秒 | 36.4996秒 | 52.6913秒 |
| 3 | 9.2134秒 | 29.0799秒 | 38.2933秒 |
| 3 | 10.1278秒 | 27.1641秒 | 37.2919秒 |
| 3 | 10.2387秒 | 28.1842秒 | 38.4229秒 |
方法 3(带有纱线的 lerna) – Node v9.10.0/yarn 1.10.1/lerna 2.11.0
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 52.3567秒 | 69.5431秒 | 121.8998秒 |
| 1 | 45.3363秒 | 56.1238秒 | 101.4601秒 |
| 1 | 40.0621秒 | 54.2408秒 | 94.3029秒 |
| 2 | 23.2312秒 | 40.1567秒 | 63.3879秒 |
| 2 | 22.7905秒 | 39.2331秒 | 62.0236秒 |
| 2 | 21.3754秒 | 37.9659秒 | 59.3413秒 |
| 3 | 13.4165秒 | 28.6476秒 | 42.0641秒 |
| 3 | 13.2283秒 | 27.9781秒 | 41.2064秒 |
| 3 | 12.6465秒 | 29.3560秒 | 42.0025秒 |
方法四(yarn 工作区)– Node v10.12.0 / yarn 1.10.1
由于yarn install会在后台自动完成“引导”步骤,因此无需执行此步骤。
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 34.9199秒 | 34.9199秒 | |
| 1 | 31.8336秒 | 31.8336秒 | |
| 1 | 32.6647秒 | 32.6647秒 | |
| 2 | 17.9583秒 | 17.9583秒 | |
| 2 | 17.7032秒 | 17.7032秒 | |
| 2 | 17.9703秒 | 17.9703秒 | |
| 3 | 12.6103秒 | 12.6103秒 | |
| 3 | 13.4137秒 | 13.4137秒 | |
| 3 | 12.8213秒 | 12.8213秒 |
方法四(yarn 工作区)– Node v11.2.0 / yarn 1.10.1
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 65.1631秒 | 65.1631秒 | |
| 1 | 69.0633秒 | 69.0633秒 | |
| 1 | 63.1915秒 | 63.1915秒 | |
| 2 | 25.6090秒 | 25.6090秒 | |
| 2 | 22.4050秒 | 22.4050秒 | |
| 2 | 24.7715秒 | 24.7715秒 | |
| 3 | 18.0540秒 | 18.0540秒 | |
| 3 | 18.8891秒 | 18.8891秒 | |
| 3 | 17.0438秒 | 17.0438秒 |
方法五(Lena 与 Yarn 工作区)– Node v11.6.0 (npm v6.5.0-next.0) / Yarn 1.12.3 / Lena 3.8.0
通过这种方法,我试图找出将yarn workspaces作为lerna配置的一部分是否与方法 4 有任何不同。由于不需要lerna bootstrap,因此相应的列为空。
但正如我所料,方法 4 没有区别,因为lerna没有参与依赖项安装/引导过程。
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 60.4779秒 | 60.4779秒 | |
| 1 | 63.3936秒 | 63.3936秒 | |
| 1 | 58.1888秒 | 58.1888秒 | |
| 2 | 32.7976秒 | 32.7976秒 | |
| 2 | 30.8835秒 | 30.8835秒 | |
| 2 | 28.9111秒 | 28.9111秒 | |
| 3 | 16.4637秒 | 16.4637秒 | |
| 3 | 17.8068秒 | 17.8068秒 | |
| 3 | 16.3400秒 | 16.3400秒 |
方法 6 (lerna + npm ci + Audit) – 节点 v10.12.0 / npm v6.4.1 / lerna 3.4.3
在这种方法中,我使用lerna和npm ci,这在持续集成环境中可以替代npm install。从lerna版本 3 开始,npm ci已成为默认的安装命令。当然,您也可以选择不使用 npm ci。
这种方法需要package-lock.json文件存在。node_modules文件夹应该已被删除,否则终端会输出警告信息。因此,UC 3 无法使用。
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 7.9733秒 | 34.1282秒 | 42.1015秒 |
| 1 | 9.3572秒 | 35.0904秒 | 44.4476秒 |
| 1 | 8.9436秒 | 36.3684秒 | 45.31200秒 |
| 2 | 10.8888秒 | 49.3526秒 | 60.2414秒 |
| 2 | 10.9077秒 | 44.9243秒 | 55.8320秒 |
| 2 | 11.5785秒 | 43.6369秒 | 55.2154秒 |
方法 6 (lerna + npm ci) – 节点 v9 / npm v5.7.1 / lerna 3.4.3
使用这个特定的npm版本,npm ci命令可用,但没有审计功能。我想测试一下这种配置,看看在不审计依赖项的情况下是否会对性能产生任何影响。再次强调,在这种情况下,UC 3 是不可行的。
| 加州大学 | 安装 | Bootstrap | 全面的 |
|---|---|---|---|
| 1 | 9.0732秒 | 29.8326秒 | 38.9058秒 |
| 1 | 9.3738秒 | 30.0418秒 | 39.4156秒 |
| 1 | 8.8552秒 | 29.1426秒 | 37.9978秒 |
| 2 | 11.7469秒 | 39.9573秒 | 51.7042秒 |
| 2 | 13.3401秒 | 44.6026秒 | 57.9427秒 |
| 2 | 13.3603秒 | 39.9416秒 | 53.3019秒 |
结论
根据我的测试, npm和yarn(工作区)在性能方面没有明显差异。从功能角度来看,两者也难分伯仲。对我而言,选择哪个包管理器完全取决于个人喜好。此外,它们可以随时切换使用,也可以结合使用。
目前,我更倾向于使用yarn workspaces作为Mono-Repo技术,因为它具备强大的依赖提升功能。另一方面,lerna及其`--hoist`标志也能实现类似的功能。我认为yarn workspaces与lerna搭配使用效果很好:配置lerna将依赖管理交给yarn workspaces,然后使用 lerna 的实用命令即可。
最初发布于doppelmutzi.github.io。
文章来源:https://dev.to/doppelmutzi/why-lerna-and-yarn-workspaces-is-a-perfect-match-for-building-mono-repos-a-close-look-at-features-and-performance--1me0






