适用范围
不会用到的情况
- 业务项目(xxx系统之类的):不会用到lerna,平时开发的项目,只需一个git仓库下放一个项目,根目录下只有一个package.json文件。
- 单包项目:不会用到lerna,比如开发一个npm包,里面包含了一些功能,也只需一个git仓库下放一个项目。
会用到的情况
- 多包项目:如果你想开发一个组件库、或大型工具库。这种项目弄成一个npm包就很不合适,需要拆分成多个npm包,这时候就是一个git仓库下,管理多个npm包。这时候用lerna就派上用场了。
多包项目
- 拿组件库举例子,如果将所有组件都放在一个git仓库下,发布到一个npm包里,用户想使用这个组件库里的某一个组件,也只能整体安装我们的组件库。但如果每个组件单独发包,比如有一个用户只用了这个组件库的button组件,用户就可以只安装button组件(npm install @my-ui/button),更灵活了。另一个好处是,我们更改另一个toast组件的代码,单包项目就得再发一次版,用户更新新包,但他使用的button却没有任何改变,就很没必要。换做多包项目,我们改了toast组件的代码,重新发toast包和整体的包。但是不用重新发button组件包(button组件包的版本号不会变),就很合适。
- 拿babel库举例子。babel库也是个典型的多包git仓库。我们去babel的github仓库看看,packages文件夹下一堆npm包,babel-cli、babel-core、babel-preset-env等等。我们作为使用者,安装自己项目需要的包即可。babel也是用lerna来管理的。
- 另一个方面,如果将这些npm包完全分开管理,一个git仓库下一个npm包,维护起来挺麻烦,包之间关联很强。还是放在一个git仓库下,用lerna进行管理比较好。
Lerna定义
Lerna是帮助我们在一个git仓库下管理多个npm包的工具。
// 初始化一个lerna管理的项目
npm i -g lerna
lerna init --independent // 关于independent下面解释
复制代码
固定模式 vs 独立模式
在初始化一个lerna管理的项目时,有固定(默认)和独立(lerna init时加–independent参数)两种模式。这两种模式关键区别是,怎样管理子包们的版本。
- 固定模式:版本号使用项目根目录下lerna.json文件中的version属性。执行lerna publish时,如果代码有更新,会自动更新此版本号的值,其他有改动子包也自动更新版本号,没改动的子包不更新版本号。也就是所有包的版本号都跟根目录下lerna.json文件中的version是有关系的。Babel库是用的这种模式。
- 独立模式:子包们的版本号独自维护,和根目录的配置没关系。每次publish时,得为每个更改的子包设置版本。
我觉得可能是这样的,对于子包之间关联性比较大的,如babel,使用固定模式合适,如组件库这种子包之间关联性不大等,使用独立模式,版本维护更自由一点。
Lerna管理的项目结构
my-ui/
package.json
lerna.json
packages/
Button/
src/
package.json
Toast/
src/
package.json
...
复制代码
Button、Toast都是独立的npm包,你也可以用vscode等编辑器只打开Button文件夹,进行该组件的开发。当然这么做也没啥必要,只是为了说明Button文件夹是个独立的子项目。我们可以在Button文件夹下执行npm install vue webpack babel等,会在Button文件夹下安装依赖包,生成node_modules文件夹 。 那么问题来了,在Toast文件夹下同样用npm install来安装包,同样有vue webpack babel等,这就比较浪费磁盘。lerna为我们提供了lerna bootstrap命令解决此问题。
Lerna Hoisting
Lerna Hoisting就是指将子包下的node_modules都提升到根目录的node_modules里,统一管理,节省磁盘。
做如下配置,Lerna Hoisting就会生效。
// 配置 lerna.json:
{
...
"npmClient": "yarn",
"useWorkspaces": true
}
// package.json配置
{
"private": true,
...
"workspaces": [
"packages/*"
],
}
复制代码
命令
lerna bootstrap
既然Button、Toast有很多公共的依赖包,那将这些依赖都放到my-ui文件夹下的node_modules,清空Button、Toast下的node_modules,就达到节省磁盘等目的了。
要实现这个,就用到lerna bootstrap,相当于为packages下所有子项目都npm install了一下,但是依赖包都安装在了my-ui文件夹下的node_modules,统一管理依赖。
lerna publish
在上面介绍固定模式、独立模式时提到了lerna publish。包开发完了,要发布到npm源上。用lerna publish命令就是发布到npm源上。根据固定模式、独立模式不同,使用不同的版本号更新策略。
配置 lerna.json:
"command": {
"publish": {
"registry": "https://registry.npmjs.org"
}
},
"ignoreChanges": [
"ignored-file",
"**/__tests__/**",
"**/*.md"
]
复制代码
- 设置 command.publish.registry:发布地址
- 设置 ignoreChanges:告诉lerna,这些文件更改,不需升级版本。
lerna add
假如我想为我的组件库下的所有子包,都安装lodash。传统做法是,到每个子包文件夹下npm install lodash。但是使用lerna,我们可以在根目录下执行命令lerna add lodash,你会看到所有等子包的package.json中dependencies字段下都有了lodash。
npm install lodash
复制代码
这里再次点明主旨,lerna方便我们管理多包项目。这种需要为所有子包安装同一个依赖的情况,使用lerna add简化了操作。
同样如果你只想给Button组件安装lodash,可以执行
lerna add lodash --scope=@my-ui/Button
复制代码
区别于直接在Button文件夹下npm i lodash,是将依赖装在了根目录的node_modules。
lerna create
如果你想新建一个lerna子包,使用
lerna create @my-ui/Input
复制代码
会在packages文件夹下生成Input文件夹,一个子项目。
my-ui/
package.json
packages/
Button/
src/
package.json
Input/
src/
package.json
Toast/
src/
package.json
...
复制代码
额外的,如果你想搞一套组件库,还可以新建一个主包,一个工具包
lerna create @my-ui/my-ui
lerna create @my-ui/Utils
my-ui/
package.json
packages/
Button/
src/
package.json
Input/
src/
package.json
Toast/
src/
package.json
Utils/
src/
package.json
my-ui/
src/
package.json
...
复制代码
Utils子包用来放一些工具函数,供其他组件使用,my-ui子包引用所有其他组件包,供想全部使用我们组件库的用户安装的包。
lerna add @my-ui/Utils --scope @my-ui/Button
lerna add @my-ui/Utils --scope @my-ui/Input
lerna add @my-ui/Utils --scope @my-ui/Toast
lerna add @my-ui/Button --scope my-ui
lerna add @my-ui/Input --scope my-ui
lerna add @my-ui/Toast --scope my-ui
复制代码
执行上面这样的命令,就可以完成子包间的引用。
总结
- 从使用场景上,如果我们开发多包项目,就适合用lerna。
- lerna是非常多明星库的选择,我们看babel、react、element-ui等等都能看到lerna。
- 你可以根据自己点需要选择固定模式或独立模式
- 包提升(Lerna Hoisting)是lerna一个非常有用的特性。
- Lerna的命令都是服务于我们方便管理多包项目,管理它们的依赖,发版时的版本号维护。
本篇文章希望能帮助您了解lerna使用场景,和它的基本使用。