lerna 背景介绍
在B端管理后台项目中,如微前端等项目,会有多个独立项目,或者是主项目下管理多个子项目,多个包互相依赖,发布时需要手动维护多个包的问题就随之而来。babel团队推出的多包管理工具lerna,解决多包依赖问题。
lerna具体如下特点:
可以管理公共依赖和单独依赖;
多package相互依赖直接内部 link,不必发版;
支持项目的单独发布和全体发布;
多包放一个git仓库,利于代码管理,如配置统一的代码规范;
模式
lerna有两种工作模式,Independent模式和Fixed/Locked模式。这两种模式有什么区别呢?lerna的默认模式是Fixed/Locked mode,在这种模式下,lerna会是把工程当作一个整体来对待,每次发布packges,都是全量发布,无论修改与否。但是在Independent mode下,lerna会配合Git,检查文件变动,只发布有改动的包。
安装lerna
yarn global add lerna
// or
npm install lerna -g
初始化项目
在一个空目录中执行如下初始化命令,会生成.git、.gitignore、lerna.json、package.json文件和packages目录
# 默认使用的是固定模式,packages下的所有包共用一个版本号
lerna init
# 如果使用独立模式,需要在init后面加一个参数。
lerna init --independent
各项目安装依赖包
# 方式一
lerna bootstrap
# 方式二,配置好workspaces后,直接项目根目录yarn(实践过)
yarn
# lerna init配置如下:
# lerna.json
{
"version": "0.0.0",
"npmClient": "yarn",
"useWorkspaces": true
}
# package.json
{
"private": true,
"workspaces": {
"packages": [
"my-app",
"my-app-child",
"my-app-child2"
]
},
"scripts": {
"start": "lerna run --parallel start",
"build": "lerna run build",
"serve": "lerna run --parallel serve",
"clean": "lerna run --parallel clean",
"prepare": "lerna run yarn",
"bootstrap": "lerna bootstrap"
},
"devDependencies": {
"lerna": "3.22.1"
}
}
以下是部分命令说明:
lerna bootstrap
lerna 提供了可以将子项目的依赖包提升到最顶层的方式 ,我们可以执行 lerna clean先删除每个子项目的 node_modules , 然后执行命令 lerna bootstrop --hoist。
lerna bootstrop --hoist 会将 packages 目录下的公共模块包抽离到最顶层,但是这种方式会有一个问题,不同版本号只会保留使用最多的版本,这种配置不太好,当项目中有些功能需要依赖老版本时,就会出现问题。
有没有更优雅的方式?再介绍一个命令 yarn workspaces ,可以解决前面说的当不同的项目依赖不同的版本号问题, yarn workspaces会检查每个子项目里面依赖及其版本,如果版本不一致都会保留到自己的 node_modules 中,只有依赖版本号一致的时候才会提升到顶层。注意:这种需要在 lerna.json 中增加配置。
"npmClient": "yarn", // 指定 npmClent 为 yarn
"useWorkspaces": true // 将 useWorkspaces 设置为 true
并且在顶层的 package.json 中增加配置
// 顶层的 package.json
{
"workspaces":[
"packages/*"
]
}
实践过:增加了这个配置后 不再需要 lerna bootstrap 来安装依赖了,可以直接使用 yarn install 进行依赖的安装。注意:yarn install 无论在顶层运行还是在任意一个子项目运行效果都是可以。
lerna.json
lerna.json的文件配置内容如下
{
"version": "0.0.0",
"npmClient": "yarn",
"useWorkspaces": true,
// "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/*"]
}
下面是部分属性的说明:
version:当前版本
npmClient:指定运行命令的客户端,设定为"yarn"则使用yarn运行,默认值是"npm"。
command.publish.ignoreChanges:通配符的数组,其中的值不会被 lerna 监测更改和发布,使用它可以防止因更改发布不必要的新版本。
command.publish.message:执行发布版本更新时的自定义提交消息。
command.publish.registry:使用它来设置要发布的自定义注册 url,而非 npmjs.org。
command.bootstrap.ignore:运行lerna bootstrap指令时会忽视该字符串数组中的通配符匹配的文件。
command.bootstrap.npmClientArgs:该字符串数组中的参数将在lerna bootstrap命令期间直接传递给npm install。
command.bootstrap.scope:该通配符的数组会在lerna bootstrap命令运行时限制影响的范围。
packages :表示包位置的全局变量数组。
其他命令
除了上面的init命令外,项目使用过程中还会用到很多其他有用的命令。
lerna create:此命令的作用是用来创建一个子包名为xx的项目。
lerna add:此命令用于安装依赖,格式为lerna add [@version] [–dev]。
lerna list:查看当前包名列表。
lerna link:将所有相互依赖的包符号链接在一起。
lerna exec:在每个包中执行任意命令。
lerna run:在每个包中运行npm脚本如果该包中存在该脚本。
lerna clean: 删除各个包下的node_modules
lerna bootstrap
# 该命令会做以下几件事:
1. `npm install` 每个软件包的所有外部依赖项;
2. 将所有 `packages` 相互依赖的Lerna `link` 在一起;
3. 在所有已安装的包里执行 `npm run prepublish`;
4. 在所有已安装的包里执行 `npm run prepare`;
在使用过程中,最经常遇到的问题是,执行
lerna bootstrap
的时候奇慢无比。这种情况通常是因为在每个独立包中都重复安装了公共依赖。在这时候,我们可以将所有公共使用的包,如react,lodash之类的移到根目录的package.json中去,并使用lerna bootstrap --hoist
命令进行安装。使用hoist
选项后,所有公共的依赖都只会安装在根目录的node_modules目录中去,而不会在每个包目录下的node_modules中都保留各自的依赖包。
发布
# 当执行 lerna publish 后会在项目根目录以及每个 packages 包下,生成 CHANGELOG.md
# 这个命令 识别出修改的包 --> 创建新的版本号 --> 修改 package.json --> 提交修改 打上版本的 tag --> 推送到 git 上。
lerna publish
lerna version --force-publish // 忽略修改强制生成版本
其他
- lerna publish 永远不会发布标记为 private 的包(package.json中的”private“: true)
- 成功案例lerna-project
坑
1、babel-loader转译问题
babel-loader@8 requires Babel 7.x (the package ‘@babel/core’). If you’d like to use Babel 6.x (‘babel-core’), you should install ‘babel-loader@7’.
# 解决
container项目package加上"@babel/core": "^7.19.6"
2、应用位置问题,放packages目录里或者根目录
// 生成.git、.gitignore、lerna.json、package.json文件和packages目录
// packages目录用于放子应用
lerna init
// package.json
{
"name": "root",
"private": true,
// 配置子应用放到packages中
"workspaces": [
"packages/*"
],
"devDependencies": {
"lerna": "^6.5.1"
}
}
// or
// 此配置时,子应用放在根目录,与package.json同级
"workspaces": {
"packages": [
"my-app",
"my-app-child",
"my-app-child2"
]
}