介绍:
公司有多个项目,每个项目有相同的依赖,或者相同的配置。依赖重复安装,多个依赖可能在多个仓库中存在不同的版本,磁盘空间占用高,开发效率也较低。每个项目都会有一个git仓库独立管理,多个项目来回切换仓库。
根据monorepo思想,多个项目可以在一个主项目中分成多个子项目,将依赖提升到公共部分进行统一管理,进行软链接处理,所有项目统一配置,建立公共组件库可以提高代码复用,从而提升开发效率,降低磁盘空间占用,依赖的版本得到了统一,方便同时版本升级,方便后期重构。
缺点也明显:
- 项目庞大,初始化耗时。
- 项目组成员负责自己的项目就会出现git权限管理问题
- 都在一个项目下就会有版本控制问题
- 规范流程问题
目前我对monorepo的使用:
- 在一个项目中使用,将本地项目拆分出多个子项目,子项目可以是demo或者不同的项目但有公共的配置和依赖,提取公共依赖,降低磁盘空间占用,共用配置,建立公共组件库可以提高代码复用,而不是用copy搬迁代码。
- 开发npm包,各个包抽离公共依赖并同步管理依赖,降低磁盘空间占用,提升开发效率。
代码结构:
my-lerna-repo/
package.json
packages/
package-1/
package.json
package-2/
package.json
learna
概念:
-
Lerna 是 babel 维护自己的 monorepo,并开源出的一个项目
-
用于管理具有多个包的 JavaScript 项目
-
Lerna 可以管理包的依赖,它可以选择使用 npm 还是 yarn,需要单独配置。
-
使用 yarn需要开启 yarn workspaces。
-
一般会把 Leran 和 yarn workspaces 结合使用。使用 Lerna 发布,使用 yarn workspaces 管理包的依赖。
-
增量构建解决构建问题。
-
拓扑排序解决包依赖问题。
优点 :
- 管理公共依赖和单独依赖
- 可单独发布和全体发布
- 多个包互相依赖直接内部link,不需要发版。
- 多包一个git仓库,统一代码规范
两种工作模式
独立模式
独立模式允许管理者对每个库单独改变版本号,每次发布的时候,你需要为每个改动的库指定版本号。这种情况下, lerna.json
的版本号不会变化了, 默认为independent
Independent 模式可以单独发版,更灵活
lerna init --independent
默认independent
固定模式
固定模式,通过lerna.json的版本进行版本管理。当你执行lerna publish
命令时, 如果距离上次发布只修改了一个模块,将会更新对应模块的版本到新的版本号,然后你可以只发布修改的库。
这种模式也是Babel使用的方式。如果你希望所有的版本一起变更, 可以更新minor版本号,这样会导致所有的模块都更新版本。
上手
全局安装
npm install lerna -g
npm install yarn -g
初始化:
lerna init
yarn init -y
配置:
lerna
和 yarn workspace
同时使用
在 lerna.json
添加:
{
"packages": [
"packages/*"
],
"version": "0.0.0",
"npmClient": "yarn", // 指定使用yarn来管理依赖
"useWorkspaces": true // 设置yarn使用workspaces管理依赖
}
然后在 package.json
添加:
{
...
"private": true, // root禁止发布,根目录一般是脚手架,不需要发布。private 是为了防止意外把内容暴露出去。
"workspaces": [ // 配置subpackage目录
"packages/*"
]
...
}
理解:yarn和lerna的配置的区别
npm 不支持 workspaces,所以之前安装依赖都要使用 yarn。
开启 yarn workspaces 之后可以在根目录中使用 yarn install
给所有的包统一安装依赖。
由于yarn和lerna在功能上有较多的重叠,我们采用yarn官方推荐的做法,用yarn来处理依赖问题,用lerna来处理发布问题。
yarn的workspace暂时并未支持按照拓扑排序规则执行命令,虽然该 rfc已经被accepted,但是尚未实现
lerna支持按照拓扑排序规则执行命令
生成子项目
lerna create xxx子项目名
添加依赖:
第一种,全局:
lerna bootstrap // 等于 yarn install,将依赖提升到根目录下的node_modules。
lerna add xxx 给全局安装
- 为每个包安装依赖
- 链接相互依赖的库到具体的目录
- 执行 npm run prepublish
- 执行 npm run prepare
第二种,指定子项目:
一般使用
lerna add 包名(可以是内部包也可以是外部包) --scope xxx子项目名 --dev
比如:
lerna add mobx --scope mobx-todo-list --dev
汇总:
// 指定子项目项目名前缀
lerna add xxx packages/前缀名-*
// 将子项目1安装到子项目2中
lerna add xx1 --scope=xx2
// 作为开发依赖
lerna add xx1 --scope=xx2 --dev
// 除了自己,将自己安装到其他所有子项目中
lerna add xx1
抽离公共模块:
xx1 和 xx2 都依赖一个包,会将两个包里面的依赖提取到根目录下的node_modules
lerna bootstrap --hoist
// 会有冲突,报错–hoist is not supported with --npm-client=yarn, use yarn workspaces instead
直接yarn吧 // 用 yarn workspace 特性替代 lerna bootstrap
查看包:
lerna list
这里是根据package.json来识别,如果里面设置了pravite:true,则无法识别到,根据情况取出该配置再执行此命令可恢复识别。
名称是根据package.json里面的名称来识别,可手动更改。
执行package.json里面的script命令
lerna run --scope 子项目名 命令
比如
lerna run --scope mobx-todo-list start
// 会执行script里面的start命令,等于
cd mobx-todo-list
yarn start
执行具体命令
lerna exec – 命令 // 对所有子项目同时执行命令
lerna exec --rm -rf ./node_modules // 删除根依赖包 同 learna clean
lerna exec --scope 子项目名 npx create react-app // 指定某个项目,执行具体命令。
建立软连接
lerna link // 同npm link
列出下次发版lerna publish
要更新的包。
lerna changed
发版
lerna publish
与使用npm在根目录下统一管理依赖的区别
npm依赖查找是会一直向上级目录查找依赖,所以可以在根目录下统一安装依赖,然后在子目录下分别安装各自的依赖。
- 依赖提升:需要手动处理,去除每个子项目里面同共的依赖项,然后删除依赖包,最后去根目录加入依赖项并安装。lerna使用命令去执行这些操作。
安装子项目依赖包:需要切到子项目目录下安装。而lerna可以在根目录下执行。 - 启动子项目:也是需要切换到子项目下,而lerna可以在根目录下执行子项目的任何命令。
- 对子项目同时执行命令:需要分别切换到子项目下执行cmd命令,而lerna可以一条命令来统一执行。比如主分应用同时启动的场景。
- lerna可以设置为全量发布和增量发布两种模式。
总的来说,lerna提供了在根目录统一操作子目录的命令来提高开发效率,其中依赖提升是主要的功能(lerna和yarn都有实现,一般使用yarn管理依赖),另外还有发布版本管理(使用lerna来管理)。
:)欢迎补充
目前遇到的问题:
- 在子项目中加入新的包,需要再次执行提取命令,不然会串包,然后程序执行错误。这时候卸载包重转吧。
lerna clean
然后再次yarn
一下重新装包。 - 需要配置子包命令,不然命令太长了。