使用lerna优雅地管理多个package

January 22, 2024

背景

对于维护过多个 package 的同学来说,都会遇到一个选择题,这些 package 是放在一个仓库里维护还是放在多个仓库里单独维护,数量较少的时候,多个仓库维护不会有太大问题,但是当 package 数量逐渐增多时,一些问题逐渐暴露出来:

  1. package 之间相互依赖,开发人员需要在本地手动执行 npm link,维护版本号的更替。
  2. issue 难以统一追踪,管理,因为其分散在独立的 repo 里。
  3. 每一个 package 都包含独立的 node_modules,而且大部分都包含 babel,webpack 等开发时依赖,安装耗时冗余并且占用过多空间。

解决

上述提到的多 package 项目,实际上是比较常见的,在日常业务中,开发 UI 组件库、Util 工具库以及插件库等场景会和上述仓库类似。接下来,我们来看一下几个明星仓库的目录结构。

https://pic1.zhimg.com/80/v2-a1e092b7246e3937b31f75004193bb08_1440w.webp 根据上述截图,我们可以发现这些仓库之间的共性:

  1. 均使用单个仓库来管理多个 package
  2. 多个 package 之间可能存在相互依赖的情况

为了降低多 package 项目的维护成本,我们使用 lerna 作为流程管理工具。

接下来,我们从一个 demo 出发,了解基于 lerna 的开发流程。

项目初始化

https://pic4.zhimg.com/80/v2-8fba50d006b2ae69e19aa202ca93ff67_1440w.webp

我们需要维护一个 UI 组件库,其包含 2 个组件,分别为 House(房子)和 Window(窗户)组件,其中 House 组件依赖于 Window 组件。 我们使用 lerna 初始化整个项目,并且在 packages 里新建了 2 个 package,执行命令进行初始化:

lerna init
shell

初始化化后目录结构如下所示:

. ├── lerna.json ├── package.json └── packages ├── house │ ├── index.js │ ├── node_modules │ └── package.json └── window ├── index.js ├── node_modules └── package.json
md

增加依赖

https://pic4.zhimg.com/80/v2-8c6637100a3c7ddebf9bef8cabe81b5b_1440w.webp

接下来,我们来为组件增加些依赖,首先 House 组件不能只由 Window 构成,还需要添加一些外部依赖(在这里我们假定为 lodash)。我们执行:

lerna add lodash --scope=house
shell

这句话会将 lodash 增添到 House 的 dependencies 属性里,这会儿你可以去看看 package.json 是不是发生变更了。 我们还需要将 Window 添加到 House 的依赖里,执行:

lerna add window --scope=house
shell

就是这么简单,它会自动检测到 window 隶属于当前项目,直接采用 symlink 的方式关联过去。

symlink:符号链接,也就是平常所说的建立超链接,此时 House 的 node_modules 里的 Window 直接链>接至项目里的 Window 组件,而不会再重新拉取一份,这个对本地开发是非常有用的。

发布到 npm

https://pic1.zhimg.com/80/v2-39b061e1852e41100c12d39b74a74220_1440w.webp

接下来我们只需要简单地执行 lerna publish,确认升级的版本号,就可以批量将所有的 package 发布到远程。

默认情况下会推送到系统目前 npm 对应的 registry 里,实际项目里可以根据配置 leran.json 切换所使用的 npm 客户端。

更新模块

https://pic3.zhimg.com/80/v2-ea7324f04c63224336b96852d00636be_1440w.webp

接下来,我们变更了 Window 组件,执行一下 lerna updated,便可以得知有哪些组件发生了变更。

→ lerna updated lerna info version 2.9.1 lerna info Checking for updated packages... lerna info Comparing with v1.0.9. lerna info Checking for prereleased packages... lerna info result - jx-house - jx-window
shell

我们可以看到,虽然我们只变更了 window 组件,但是 lerna 能够帮助我们检查到所有依赖于它的组件,对于没有关联的组件,是不会出现在更新列表里的,这个对于相比之前人工维护版本依赖的更新,是非常稳健的。

集中版本号或独立版本号

截止目前,我们已经成功发布了 2 个 package,现在再新增一个 Tree 组件,它和其他 2 个 package 保持独立,随后我们执行 lerna publish,它会提示 Tree 组件的版本号将会从 0.0.0 升级至 1.0.0,但是事实上 Tree 组件仅仅是刚创建的,这点不利于版本号的语义化,lerna 已经考虑到了这一点,它包含 2 种版本号管理机制。

  1. fixed 模式下,模块发布新版本时,都会升级到 leran.json 里编写的 version 字段
  2. independent 模式下,模块发布新版本时,会逐个询问需要升级的版本号,基准版本为它自身的 package.json,这样就避免了上述问题

如果需要各个组件维护自身的版本号,那么就使用 independent 模式,只需要去配置 leran.json 即可。

总结

https://pic1.zhimg.com/80/v2-549e7ecc4fe70c64a41493dc42e5e82c_1440w.webp

lerna 不负责构建,测试等任务,它提出了一种集中管理 package 的目录模式,提供了一套自动化管理程序,让开发者不必再深耕到具体的组件里维护内容,在项目根目录就可以全局掌控,基于 npm scripts,可以很好地完成组件构建,代码格式化等操作,并在最后一公里,用 lerna 变更 package 版本,将其上传至远端。

如果你有类似的项目,那么现在是时候尝试用 lerna 做优化了。