Open WJCHumble opened 2 years ago
turborepo 确实好用,我也选的: PNPM + Turborepo + Changesets这个组合。
不过可以看看 nx ,基本能够 cover turbo 的所有功能,现在 lerna5+ 基本都是调用 nx 的基础能力。
其他几个都不好用。
Vercel 团队成员可都太夸张了,大佬云集
Guillermo Rauch(创始人): socket.io / mongoose 的作者 Sebastian Markbage : 原 React 团队 Tech Lead Rich Harries: sveltejs 作者 & rollup 作者 Donny/강동윤:swc作者 Tobias Koppers : webpack 作者 Schlez : 原 Wix 工程师 & fnm 作者 Alexander Akait : webpack 核心贡献者 & prettier 贡献者 Jared Palmer : Turborepo 创始人 Ethan Arrowood : nodejs & fastifyjs 库维护者 Broooooklyn : napi.rs 作者
对了,你为啥用 issue 写博客啊
@hooper-hc Issue 算是一个存档,掘金、思否和知乎都有(搜五柳)
前言
相信很多关注 Monorepo 生态的同学,应该大都看过这篇文章 monorepo.tools,其中列举了现存的几个主流的 Monorepo 相关的工具:
相应地,在这篇文章中也对各类工具进行了一一介绍。并且,我相信每个看过这篇文章的同学,都会留下这么个疑问:这么多 Monorepo Tool,我要如何进行选型?
这里,我给出的答案是 PNPM + Turborepo + Changesets。那么,又为什么是这 3 者呢?下面,我将会分别围绕这 3 个技术展开,来一一解答这个选型的原因以及怎么做。
PNPM
PNPM 的动机(Motivation),如它在官方文档介绍的所说:“Saving disk space and boosting installation speed”,节省磁盘空间和提高安装速度。除开这个动机描述的显著优点外,PNPM 内置了对 Monorepo 的支持,并解决了很多令人诟病的问题。
其中,比较经典的就是 Phantom dependencies(幻影依赖)。由于,默认情况下
yarn
、npm
安装的依赖都是会被提升。所以,有时候你可能会遇到 Monorepo 项目中的某个包中的 package.json 没有安装这个依赖,结果实际代码中却使用了这个依赖...虽说,PNPM 可以解决这个问题,但是,默认情况下 PNPM 安装的依赖也是会被提升的。如果,需要 PNPM 禁止依赖提升,我们可以通过在 Monorepo 项目工作区下的
.npmrc
文件中 配置,例如只提升lodash
:当然,还有一些其他的问题,有兴趣的同学可以看 ELab 团队写的这篇文章《Monorepo 的这些坑,我们帮你踩过了!》。
那么,在简单解答了为什么用 PNPM 后,下面我们来看一下要怎么用?
Workspace 配置
要使用 PNPM 的 Monorepo 很简单,只需要在 Monorepo 项目的工作区下新建
pnpm-workspace.yaml
文件并配置:接下来,则是记忆常用依赖和多包任务执行相关的命令。由于,我们的技术选型中有 Turborepo,它会负责多包任务的执行。所以,这里只需要记忆常用依赖相关的命令。
常用依赖相关命令
pnpm i
在 PNPM 中,安装依赖可以用
pnpm i
来完成。在 Monorepo 的场景下,默认情况下pnpm i
会安装所有的依赖(包括packages/*
)。此外,pnpm i
还需要用到 3 个选项(Option):--filter <package>
,安装依赖到指定的package
,不声明要安装的依赖包则默认安装package.json
中的所有依赖--prod, P
,安装依赖到dependencies
--dev, D
,安装依赖到devDependencies
pnpm remove
在 PNPM 中,删除在
package.json
中的某个依赖,可以用pnpm remove
完成。它的选项(Option)使用和pnpm i
大同小异。其中,不同地是当我们在工作区想要删除packages
中所有包的package.json
中的某个依赖的时候,需要使用-r
,例如移除所有包中的lodash
:Changesets
经常维护开源项目的同学都知道的一点,每次包(Package)的发布,需要修改 package.json 的
version
字段,以及同步更新一下本次发布修改的 CHANGELOG.md。这么一来,就会凸显一个问题,每次发布都需要手动地去更新
version
、更新 CHANGELOG.md,未免有点繁琐。并且,用过 Lerna 的同学,应该都知道 Lerna 内置了对这块的支持。但是,无论是 PNPM 又或者是下面要说的 Turborepo 都不支持这块,所以 2 者的官方文档都给大家推荐了用于支持这块能力的工具,例如 Changesets、Beachball、Auto 等。
那么,这里我们要介绍的就是 Changesets。下面,我们来看一下在前面建好的 PNPM 的 Monorepo 项目中如何使用 Changesets。首先,需要执行在 Monorepo 项目的工作区下,执行如下 2 个命令:
前者是安装 Changesets 的 CLI,后者是初始化 .changeset 文件夹以及对应的文件:
这里,我们来看一下 config.json 文件:
除开
$schema
这个不需要修改的字段, config.json 文件中列了 7 个字段,各个字段分别代表的作用为:changelog
设置 CHANGELOG.md 生成方式,可以设置false
不生成,也可以设置为定义生成行为的文件地址或依赖名称,例如 Changsets 提供的changelog-git
。其中,定义生成行为的文件固定代码模版为:async function getDependencyReleaseLine() {}
export default { getReleaseLine, getDependencyReleaseLine }
可以看到,如果在前面说的这 2 种情况下获取不到
registry
的话,Changesets 都是按公共的 Registry 去查找或者发布包的。Turborepo
说起 Turborepo,可能大家会有点陌生。但是,对于 Vercel 我想大家都知道(毕竟 Rich Harris、Sebastian Markbåge 等都加入了),Turbrepo 则是 Vercel 旗下的一个开源项目。Turborepo 是用于为 JavaScript/TypeScript 的 Monorepo 提供一个极快的构建系统,简单地理解就是用 Turborepo 来执行 Monorepo 项目的中构建(或者其他)任务会非常快!
所以,你可以理解成快是选择 Turborepo 负责 Monorepo 项目多包任务执行的原因。而在 Turborepo 中执行多包任务是通过
turbo run <script>
。不过,turbo run
和lerna run
直接使用有所不同,它需要配置turbo.json
文件,注册每个需要执行的script
命令。在 Turborepo 中有个 Pipelines 的概念,它是由
turbo.json
文件中的pipline
字段的配置描述,它会在执行turbo run
命令的时候,根据对应的配置进行有序的执行和缓存输出的文件。举个例子,通常情况下我们一个 Monorepo 项目中的每个包可能会有
dev
、build
、test
、clean
等 4 个命令,那么对应的turbo.json
的配置会是这样:可以看到,
pipeline
中的每个key
则对应着每个需要执行的turbo run
命令的名称,其中dependsOn
、outputs
、cache
等 3 个字段分别作用为:dependsOn
表示当前命令所依赖的命令,^
表示dependencies
和devDependencies
的所有依赖都执行完build
,才执行build
outputs
表示命令执行输出的文件缓存目录,例如我们常见的dist
、coverage
等cache
表示是否缓存,通常我们执行dev
命令的时候会结合watch
模式,所以这种情况下关闭掉缓存比较切合实际需求这样一来,我们就可以使用诸如
turbo run build test
的命令,它则会按pipeline
的配置依次执行对应的命令。当然,如果你想每个命令都支持单独执行,可以直接配置为
{}
即可。此外,如果要使用turbo run
命令,还需要在 package.json 中声明packageManage
字段为指定的包管理工具及版本,例如"packageManager": "pnpm@6.30.0"
。结语
阅读到此处,我想大家应该理解了 PNPM + Turborepo + Changesets 这个技术选型的原因以及要怎么做。当然,这个选型只是我个人的思考所得出的答案,相信也有同学仍然钟情于 Lerna,又或者喜欢 Rush 一把梭,这些观点并无对错,本质上这也是编程的魅力所在,各个轮子都有其存在的价值。
最后,如果文中存在表达不当或错误的地方,欢迎各位同学评论交流~