痛点
假设我们要把一些通用能力拆成npm 包,目前有ted-cli
和ted-util
两个包,其中ted-cli
依赖ted-util
。那么ted-util
发布新版本之后,是不是ted-cli
应该安装一下,测试看看有没有bug?其实npm link 命令就是来解决本地开发依赖包的问题的。但如果你的项目里有几十个包互相依赖,这种手工link 的方式就比较捉襟见肘了。
更重要的一点是,ted-cli
和ted-util
都是ted 这个项目下的一些包,更好的组织方式应该是把他们放在一个git 仓库里,这样做的好处就是代码管理收敛,也能减少各种配置文件的泛滥和不一致,包括lint、tsconfig 等。
下面要介绍的两个东西,就是来解决这个问题的。Monorepo 是一种代码组织方式,Lerna是用来更好的管理这种组织的工具。
Monorepo
Monorepo 的全称是 monolithic repository,即单体式仓库,与之对应的是 Multirepo(multiple repository),这里的“单”和“多”是指每个仓库中所管理的模块数量。
Multirepo 是比较传统的做法,即每一个 package 都单独用一个仓库来进行管理。例如:Rollup, …
Monorep 是把所有相关的 package 都放在一个仓库里进行管理,每个 package 独立发布。例如:React, Angular, Babel, Jest, Umijs, Vue …
Lerna
Lerna 是什么
Lerna 是一个管理多个 npm 模块的工具,是 Babel 自己用来维护自己的 Monorepo 并开源出的一个项目。优化维护多包的工作流,解决多个包互相依赖,且发布需要手动维护多个包的问题。
使用教程
1.安装
npm i -g lerna
2.初始化项目
mkdir lerna-demo cd lerna-demo lerna init
初始化之后的目录结构如下,其中lerna.json是用来存放lerna 配置的文件。
. |-lerna-demo |-lerna.json |-package.json |-packages
3.创建模块文件夹
#lerna create lerna create ted-cli lerna create ted-utils
我们可以看到,lerna 帮助我们在packages 文件夹下初始化了两个包,包含了一些基础的文件和测试文件,我们只用集中注意力开发即可。
. |-lerna-demo |-lerna.json |-package.json |-packages | |-ted-cli | | |-README.md | | |-__tests__ | | | |-ted-cli.test.js | | |-lib | | | |-ted-cli.js | | |-package.json | |-ted-utils | | |-README.md | | |-__tests__ | | | |-ted-utils.test.js | | |-lib | | | |-ted-utils.js | | |-package.json
4.安装依赖
使用 bootstrap 命令,可以一键安装所有module 的依赖,更重要的是,bootstrap命令会默认自动执行npm link,把ted-utils
等包link 到node_modules 里,相互依赖的包,相当于是直接使用的的package 里的包,调试和开发十分方便。
另外,有的时候,我们可能会在每个子项目中都依赖相同的第三方包,例如lodash 之类,这时候node_modules 里会有很多重复的包,造成浪费存储等问题,可以使用–hoist 参数,将重复的node_modules提升到根目录的node_modules里,因为node 包是会向上查找的,所有不影响使用。
注意,如果在package.json 的scripts 中用到一些开发时依赖的工具,如webpack 等,因为只会在当前目录下查找,最好还是安装一下,或使用bin文件路径执行
lerna bootstrap lerna bootstrap --hoist
5.发布npm 包
Lerna 管理包其实有两种模式,Independent mode和Fixed/Locked mode,默认是Fixed。
Fixed 模式:
仓库下所有包的版本都是一致的,每次发布,会更新所有包的版本号。这个版本号存在了lerna.json
的version 字段。这种方式我觉得适合一些高内聚的仓库,就是子包的依赖关系非常强烈那种。
Independent 模式:
这种方式,每次在发布的时候,lerna会检测有变更的包,只发布有变更的包,包的版本号根据package.json 的version 递增。
因为默认是Fixed模式,是在init 的时候确定的,如果init 后想变更也很简单,更改erna.json
的version 字段为independent即可。
//初始化的时候带上参数可以使用不同模式 lerna init lerna init --independent
说回正题,发布其实很简单,执行publish 就可以,会有交互式的询问版本增加方式。当然,先登录你的npm 账号。
当然,如果是内网发布,需要增加几个参数。
lerna publish // 内网发布 lerna publish --no-verify-access --no-verify-registry
值得注意的是,npm 其实是有prepublish 之类的一些钩子的,我们可以好好利用下,因为有的包需要build 忘记build 可能会发布一个坏包出去。所以在prepublish 这里其实可以执行一下构建和单元测试等命令。
相关命令
#创建工程 lerna init lerna init --independent #安装依赖 #2 所有的包都装上 lerna add eslint #2 只有某个包装上 lerna add eslint --scope=package1 #2 只有某些包装上 lerna add eslint packages/prefix-* #创建类库软链 #下拉远程依赖 lerna bootstrap #导入本地某包 lerna import #发布某包 lerna publish lerna publish --npm-tag latest lerna publish --npm-tag v1.0.0 lerna publish --canary lerna publish --skip-git #检查变化 lerna changed lerna diff [package?] #运行命令 lerna run [script] #列出类库 lerna list
参考文档
- https://lerna.js.org/
- https://github.com/pigcan/blog/issues/3
- https://www.jianshu.com/p/d21cd4717542
- https://juejin.im/post/5a989fb451882555731b88c2
- https://juejin.im/post/5d4aa8905188250e4258249e#heading-0
- https://juejin.im/post/5c7491cae51d457fc564d6ee#heading-17
- https://github.com/chinanf-boy/lerna-zh
- https://blog.logrocket.com/setting-up-a-monorepo-with-lerna-for-a-typescript-project-b6a81fe8e4f8/
- https://www.javazhiyin.com/52734.html
- https://github.com/babel/babel/blob/master/lerna.json