使用lerna构建monorepo项目
PS:lerna已经不维护了,声明在这里:github.com/lerna/lerna…
可以改用nx
使用lerna构建monorepo
monorepo是什么?
monorepo就是在一个git repository里面管理多个packages或者项目
在实际开发使用中,在哪些场景下可能你会想要使用这种代码管理的方式呢?
- 你们团队是一个以全栈开发为目标的团队,可以将前后端的项目放在一个repository里面进行管理
- 如果你是想使用微服务的,无论是后端微服务还是前端微服务(Single-SPA)之类的,你就可以在一个repository里面管理多个相关的项目。
- 开源项目中对项目模块分开管理和发布,例如create-react-app
Lerna
monorepo是一种代码管理的概念,有很多工具都可以帮助你去实现,这里介绍的是Lerna
那么Lerna是什么呢?
官方解释是:
Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.
Lerna在提供了一些对多个package进行操作的命令之外,还提供了一些和对项目发布工作流程的一些支持。
我们这里使用的lerna v3。
初始化项目
要从一个新项目开始还是从已有项目出发,都可以使用lerna去创建monorepo的开发模式
lerna提供了两种方式去管理项目,一种是Fixed/Locked mode (default),另一种是Independent mode
区别就是,Fixed/Locked mode的版本控制是整一个repository作为一个整体的,而Independent mode则每个package的版本控制是独立的
我们这里介绍的是Independent mode的形式
执行下面命令对lerna进行初始化
npx lerna init --independent
会生成一个lerna.json
的文件,这就是lerna的配置文件,内容如下
{
"packages": [
"packages/*"
],
"version": "independent"
}
这里有两个配置,一个是packages,可以用来指定你需要管理的package的目录,默认配置是packages下面的第一级目录下的项目文件夹
如果要嵌套多层,可以改成
{
"packages": [
"packages/**"
]
}
如果需要添加其他目录,例如前后端一起的monorepo,可以修改为
{
"packages": [
"servers/*", "frontend/*"
]
}
具体的目录结构就视具体而定。
如果你是习惯使用yarn进行包管理的,我们还可以定义我们需要的包管理工具为yarn
{
"version": "1.1.3",
"npmClient": "npm",
"packages": ["packages/*"]
}
具体的详细的lerna.json
配置可以看lerna github上的Readme文档
执行package script
我们可以使用lerna run
指令,去运行每个包中包含有相关package script的相关命令。
例如,我们有两个前端package,里面的package.json中都有一个start
{
"srcipts": {
"start": "react-scripts start",
}
}
我们则可以直接运行
lerna run --parallel start
--parallel
参数就是指为项目的中需要一直的进程,打印所有子进程的输出。说得有点绕,理解为需要一直运行的就加上这个参数就可以了。
包依赖
安装包
使用下面命令,可以安装所有包的package.json
中的dependencies
npx lerna bootstrap
公共的包
我们可以将公共的包安装在根目录下的package.json
下,例如,你是前端微服务的项目而且是使用react作为主要技术栈的,可以把react
、react-dom
等安装在根目录下,packages都是可以直接引用的
更常用的是一些项目规范的配置,例如eslint
、prettier
、tsconfig
之类的,也可以直接安装在根目录下
把公共包提取出来的好处有:
- 所有包用的依赖包版本都是一致的
- 做一些包的升级和像github进行包检查时,更方便地进行升级
- 安装依赖包的时间可以更少
- 需要更少的存储空间
项目中包与包之间的引用
如果我们packages里面需要进行互相引用
我们使用lerna add
命令,可以为指定的包安装第三方的或者本地的包,这个指令和yarn add
或者npm install
实质上是类似的,下面是一些来自文档的一些例子
# Adds the module-1 package to the packages in the 'prefix-' prefixed folders
lerna add module-1 packages/prefix-*
# Install module-1 to module-2
lerna add module-1 --scope=module-2
# Install module-1 to module-2 in devDependencies
lerna add module-1 --scope=module-2 --dev
# Install module-1 to module-2 in peerDependencies
lerna add module-1 --scope=module-2 --peer
# Install module-1 in all modules except module-1
lerna add module-1
# Install babel-core in all modules
lerna add babel-core
版本控制
lerna的版本控制依赖于相应git branch上面的tag,来判断修改记录和进行相应的版本控制
这里一开始踩了坑,lerna是不支持git flow的,作者认为如果按照git flow的话,是一种anti-pattern,也就是反例。
相关的issue:github.com/lerna/lerna…
因为我们使用的是independent mode,所以我们需要对每个包的版本进行管理。如果项目使用了一些CI workflow的话,我们还需要将版本控制放到CI流程中去。
lerna进行版本控制的话,是使用lerna version
命令。
具体做了几个事情:
- 定义了从上一个git branch release tag开始,哪些包被更新了
- 更新版本号
- 修改包的
package.json
里面的version
, 运行在根目录下和每个更新包中的npm lifecycle scripts。提交这些修改,并打上release tag - 推送到远程git repository
因此,lerna已经能为我们完成打release tag和更新changelog的工作。
最后,根据git flow,我们只需要将release分支合并回development分支,根据需要看是否需要hotfix release的代码,然后在合并到master/main分支,则完成了一次release
如果你需要有prerelease、beta的,也是类似的进行版本控制。lerna version
提供了参数可以完成
--conventional-prerelease
: 当前release是prerelease版本.--conventional-graduate
: 把prerelease版本的包变成稳定版本的包版本.
管理发布
最后,如果我们还需将包发布到npm或者其他registry上面,则需要用到lerna publish
命令。
注意,如果你不是所有包都需要发布,可以在不需要发布的包中把
package.json
的private
设为true
publish命令有两种情况可选
# 发布在当前commit head下,被打了tag的包
lerna publish from-git
# 发布在当前npm registry下,没有该版本的包
lerna publish from-package
在publish的时候,有一个比较重要的参数--canary
。
这个参数的意思是,在发布到npm之前,先在当前版本创建一个新版本,将minor版本加上1,还有加上alpha后缀,例如1.0.0会变成1.1.0-alpha)。如果需要指定后缀,则可以使用--preid
参数进行定义,例如--preid beta
总结
至此,我们已经介绍了lerna如果创建一个简单的monorepo的项目,并进行脚本运行,包的安装和依赖,版本控制和发布。
剩下的交给你们。
转载自:https://juejin.cn/post/7085533287858307102