前置知识:会使用或了解npm,yarn,pnpm等工具之一。 不想看背景和为什么的,请直接看实践部分
近来对tripdocs编辑器项目(已开源)进行重构,目标是使他能够按需加载指定的功能。因为要让插件能够分开加载,所以我需要把插件打包多个npm包。这时候,一个问题来了,多个git仓库还是一个git仓库。
于是我参考了多个开源项目,最终选择了一个git仓库,多个项目。即现在流行的monorepos
。
tripdocs编辑器项目是基于携程内部在线文档编辑器内核,提炼的一款通用的,现代的、稳定的、支持协同的、可用于生产环境的在线文档编辑器
选择monorepos
的原因有多个:
不用配置多个仓库,一个仓库搞定所有。新成员下载项目的时候,下载一个仓库就够了。
有时候,一个改动会涉及多个包,如果使用multirepos会带来一些麻烦。
比如,multirepos会导致分仓库的star数量远低于主仓库。然后开源的开发者更乐意去star数量多的主仓库。
当然,monorepos也有缺点,比如主仓库会变大,这样IDE加载时间会变长。。(我觉得完全能接受)
因为pnpm显著加快了安装依赖的速度,减少了依赖包占用的电脑硬盘空间。
然后还因为npm和yarn做多包管理,存在两个问题。
phantom dependencies指的是某个包没有被安装(package.json
中没有,但能够引用它)。
这是因为,如果使用npm和yarn做多包管理,a包依赖b包,b包依赖c包。node_modules
下结构如下:
// a包下面b和c是同级的
node_modules
b
c
所以你这个时候require('c')是可以运行的。
npm doplgangers指的是相同版本依赖包重复安装的现象。
在a包依赖b包,b包依赖c包(版本2)的基础上,如果a包依赖c包(版本1)。此时再安装一个d包依赖c包(版本2)。
// a包下面b和c是同级的,
node_modules
b
node_modules
c(版本2)
c(版本1)
d
node_modules
c(版本2)
此时,c(版本2)就重复安装了2次,大型项目中,这很常见。他们会侵蚀你的大量电脑硬盘空间。
以上两个问题,你可以很简单的验证出来,我提供了一个demo
{
"name": "testphantom",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.11", // c包
"glob": "^8.1.0", // d包
"minimatch": "^6.1.6" // b包
}
}
特别的,对于npm doplgangers,你可以观察一下node_modules下glob和minimatch的node_modules中的brace-expansion。lock文件中也有体现
2022年之前提到了 pnpm 因为软连接而不能使用的场景:
2022年之后官方提出了解决方案,退回yarn的包管理模式,支持无符号链接的 hoisted 的node_modules(从v6.25.0开始)
项目目录
packages/
foo
...
package.json
core
...
package.json
...
pnpm-workspace.yaml
文件,内容如下 ``` packages:all packages in direct subdirs of packages/
在core中package.json中使用 "foo": "workspace:../foo"
引入包。当然,你也可以这样:
{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}
假设他们package中的版本都是1.5.0,利用pnpm发布(pnpm publish)的时候,他们会被转化为
{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}
特别的:如果你写明了版本,比如
"zoo": "^1.5.0"
,那么要检查本地pnpm-workspace_yaml
声明的范围内,是否存在符合规则的版本。如果有,会从本地加载,否则会从远程npm仓库安装。
此时,我已经可以从core中引用foo的代码了。
so easy.