维护过多个package项目的同学可能都会遇到一个问题:package是放在一个仓库里维护还是放在多个仓库里单独维护。当package数量较少的时候,多个仓库维护不会有太大问题,但package数量逐渐增多时,一些问题逐渐暴露出来:
正是在这一需求背景下,babel团队推出的多包管理工具lerna,旨在优化基于Git+npm的多package项目的包管理方式。 像现在流行的vue-cli, create-react-app 等脚手架工具都有用到lerna。lerna是架构优化的产物,而架构优化的主要目标是以提高ROI为核心的多package管理。
作为一种多包依赖解决方案,lerna具体如下特点:
lerna有两种工作模式,Independent模式和Fixed/Locked模式。这两种模式有什么区别呢?lerna的默认模式是Fixed/Locked mode,在这种模式下,lerna会是把工程当作一个整体来对待,每次发布packges,都是全量发布,无论修改与否。但是在Independent mode下,lerna会配合Git,检查文件变动,只发布有改动的包。
使用lerna之前,需要全局安装lerna,安装的命令如下:
yarn global add lerna
#or
npm install lerna -g
如果是在已经存在的项目中安装lerna,使用下面的方式:
lerna bootstap
使用lerna 初始化项目的方式和使用npm方式类似。首先,我们在一个空目录中执行如下初始化命令。
lerna init
默认使用的是固定模式,packages下的所有包共用一个版本号,如果使用独立模式,需要在init后面加一个参数。
lerna init --independent
执行上面的命令后,lerna会创建一个lerna.json配置文件和packages文件夹,此时项目的目录结构如下。
lerna-repo/
packages/
package.json
lerna.json
lerna.json中的packages配置是一个通配符列表,用于匹配包含了package.json的的目录。
其中,lerna.json的文件配置内容如下:
{
"version": "1.1.3",
"npmClient": "npm",
"command": {
"publish": {
"ignoreChanges": ["ignored-file", "*.md"],
"message": "chore(release): publish",
"registry": "https://npm.pkg.github.com"
},
"bootstrap": {
"ignore": "component-*",
"npmClientArgs": ["--no-package-lock"]
}
},
"packages": ["packages/*"]
}
下面是部分属性的说明:
除了上面的init命令外,项目使用过程中还会用到很多其他有用的命令。
接下来,新建moduleA和moduleB两个模块, 并且moduleA需要依赖moduleB。
lerna create module-a
lerna create module-b
在module-a中引入module-b,module-a代码如下。
// module-a
'use strict';
const moduleB = require('module-b');
console.log('moduleB:', moduleB());
module.exports = moduleA;
function moduleA() {
return 'it's module a';
}
module-b的代码如下。
// module-b
'use strict';
module.exports = moduleB;
function moduleB() {
return 'it's module b';
}
此时,我们运行node packages/module-a/lib/module-a.js 可能会报错,提示找不到module-b模块,这是因为我们还没在moduleA中安装依赖,需要使用下面的命令安装依赖。
lerna add module-b --scope=module-a
可以看到,module-a node_modules目录下安装了module-b包,重新运行上面的module-a代码会输出如下内容。
$ node packages/module-a/lib/module-a.js
moduleB: it's module b
执行发布的时候,需要Git工具的配合,请确认此时该lerna工程已经连接到Git的远程仓库。如果是第一次发布,可能需要先执行如下命令。
npm login --registry=https://registry.npmjs.org
然后,我们将module-a、module-b进行统一改个名称,比如统一加上@m_alfred前缀。
@m_alfred/module-a
@m_alfred/module-b
然后,执行在输入用户名及密码之后执行发布命令。
lerna publish
发布完成之后,即可在npm仓库看到对应的提交信息。
$ lerna publish
lerna notice cli v3.22.1
lerna info current version 0.0.2
lerna info Looking for changed packages since v0.0.2
? Select a new version (currently 0.0.2) Patch (0.0.3)
Changes:
- @m_alfred/module-a: 0.0.2 => 0.0.3
- @m_alfred/module-b: 0.0.2 => 0.0.3
? Are you sure you want to publish these packages? Yes
lerna info execute Skipping releases
lerna info git Pushing tags...
lerna info publish Publishing packages to npm...
lerna info Verifying npm credentials
lerna http fetch GET 200 https://registry.npmjs.org/-/npm/v1/user 1257ms
lerna http fetch GET 200 https://registry.npmjs.org/-/org/m_alfred/package?format=cli 1405ms
lerna info Checking two-factor auth mode
lerna http fetch GET 200 https://registry.npmjs.org/-/npm/v1/user 408ms
lerna success published @m_alfred/module-b 0.0.3
lerna notice
lerna notice 📦 @m_alfred/module-b@0.0.3
lerna notice === Tarball Contents ===
lerna notice 1.1kB LICENSE
lerna notice 94B lib/module-b.js
lerna notice 584B package.json
lerna notice 116B README.md
lerna notice === Tarball Details ===
lerna notice name: @m_alfred/module-b
lerna notice version: 0.0.3
lerna notice filename: m_alfred-module-b-0.0.3.tgz
lerna notice package size: 1.3 kB
lerna notice unpacked size: 1.9 kB
lerna notice shasum: f530a9abcab3373758574f5b48c6f9eb65788d2a
lerna notice integrity: sha512-KFEkbN8COpoSj[...]2LRs0hY5U+z2w==
lerna notice total files: 4
lerna notice
lerna http fetch PUT 200 https://registry.npmjs.org/@m_alfred%2fmodule-b 3077ms
lerna success published @m_alfred/module-a 0.0.3
lerna notice
lerna notice 📦 @m_alfred/module-a@0.0.3
lerna notice === Tarball Contents ===
lerna notice 1.1kB LICENSE
lerna notice 177B lib/module-a.js
lerna notice 620B package.json
lerna notice 116B README.md
lerna notice === Tarball Details ===
lerna notice name: @m_alfred/module-a
lerna notice version: 0.0.3
lerna notice filename: m_alfred-module-a-0.0.3.tgz
lerna notice package size: 1.3 kB
lerna notice unpacked size: 2.0 kB
lerna notice shasum: 27a2413b6761b76548ac199aa2df0377768715f5
lerna notice integrity: sha512-2H1QB5FdRaAuR[...]p7RYrOI2QdjSQ==
lerna notice total files: 4
lerna notice
lerna info lifecycle root@undefined~publish: root@undefined
lerna http fetch PUT 200 https://registry.npmjs.org/@m_alfred%2fmodule-a 6363ms
> root@undefined publish /Users/alfred/workspace/jack-bean/lerna-example
> lerna publish
lerna notice cli v3.22.1
lerna info current version 0.0.3
lerna notice Current HEAD is already released, skipping change detection.
lerna success No changed packages to publish
Successfully published:
- @m_alfred/module-a@0.0.3
- @m_alfred/module-b@0.0.3
lerna success published 2 packages
发布成功后,单独修改moduleB代码,然后再执行发布命令。
Changes:
- @m_alfred/module-a: 0.0.3 => 0.0.4
- @m_alfred/module-b: 0.0.3 => 0.0.4
可以看到,module-a版本也升级了,这是由于我们l在初始化erna init的时候使用的是默认的Fixed mode模式,所以packages下所有包都会使用同一版本号。
在开发过程中,很多模块都会依赖babel、eslint等模块,这些大多都是可以共用的。此时,我们可以通过lerna link convert命令将各个包package.json中共同的devDependencies移动到根目录的package.json中,将它们提升到项目根目录中,这样做的好处有:
下面是使用lerna的完整工作流程图。
参考:http://www.febeacon.com/lerna-docs-zh-cn/routes/basic/about.html