lerna + yarn workspace

常见的npm包代码的管理分为两种方案

  • multirepo:把项目按模块拆分为多个库
  • monorepo:把所有项目放在单个仓库中

multirepo

  • 优点:有自己的仓库,可以使用擅长的技术,工具,工作流程等,效率比较高
  • 缺点:代码质量得不到保证,如果项目之间存在依赖关系,修复某个项目得到bug需要同步到其他的项目,依赖相互引用的话 引用方式需要考虑

monorepo

  • 优点:能够统一管理代码,代码风格以及质量能够得到保证。
  • 缺点:都用同一种技术方案,效率可能会降低。

真正的需要考虑的是 规范大于一切,无规矩不成方圆,如果风格各异并不便于团队开发,互相做版本迭代的时候 同样会降低开发效率。并且如果A依赖于B,B发现了BUG并且发版本hotfix,那么A同样也需要进行发版。维护会非常困难。

而monnorepo模式可以依赖工具,根据module之间的关系,同时发布A,B的依赖版本。

  • lerna create [package] 创建一个package到项目的packages下
  • lerna add [module] 为每个package都安装依赖
lerna add react
  • 为指定的package安装依赖
lerna add react-dom packages/package1
//or 
lerna add react-dom --scope=package1
  • 添加依赖到根目录 node_module中
npm install typescript -d
  • package之间的互相依赖(会在package/package.json下添加依赖):
lerna add package2 --scope package1
// or
lerna add package2 package/package1
  • lerna bootstrap用于将packages连接在一起(前提是相互依赖的库),并安装到package下的依赖搭配package/node_modules
  • lerna clean 删除各个包下的node_modules 不会删除根目录的node_modules
  • lerna ls列出当下lerna仓库下的所有公共包
  • lerna run test运行每个包下面的script 的test命令
  • lerna run test —scppe package1 单独运行package1的test命令
  • lerna changed检查自上次发布以来哪些软件包被修改过
  • lerna link 连接相互引用的库,当package/package.json内明确了packages下的包的时候,才会将相关的包链接到package/node_modules中
  • lerna info 查看lerna及运行环境信息

版本发布

packages下的包版本发布需要使用lerna publish,这个命令组合了两个命令,lerna-version和npm publish。

其中lerna-version针对lerna的管理模式(固定模式和独立模式),在表现上是不同的。

但主要工作还是在进行npm publish之前,去管理哪些包要进行发布,以及发布过程中生成的Git commit,git tag的提交。

  1. 固定模式下的lerna verison:
    • 找出从上个版本发布以来有所变更的package
    • 根据当前lerna.json中的版本生成新的版本号
    • 更新涉及到变更package下的package.json版本号
    • 更新lerna.json文件中的版本号
    • 将version更新,生成CHANGELOG.md文件带来的变动提交为一次commit
    • 基于这次commit为所有涉及到更新的package打上各自的tag
    • 推送commit和tags到远程仓库
  2. 独立模式下的lerna version
    • 找出上一个版本发布以来有所变更的package
    • 提示开发者为需要更新的package选择要发布的版本号
    • 更新到package下的package.json version版本号
    • 如果packages下其他包有依赖这个包,那么这些包下的package.json中此包版本也会更新
    • 将version的更新变动提交为一次commit
    • 基于这次commit为所有涉及到更新的package打上各自的tag
    • 推送commit和tags到远程仓库

初次使用发布时可能会遇到以下的问题和注意事项

  1. 避免开发者自己去打tag.lerna发布时会自动生成tag,并且查找更新是基于tag来识别的,避免开发者手动打上tag后,影响lerna查找变更,可能会造成一些变更包没有按照预期发布
  2. 避免多条分支同时进行。在多条分支同时进行时,可能会生成相同的版本号,从而发生版本冲突。解决方法:分支开发者之前应该提前约定版本信息
  3. lerna publish发布途中失败了,如果重新发布。有时候发布可能会失败,再次运行lerna publish时,因为tag已经打上了,无法再次查找更新,进行包的发布。

可以采用以下两种发布方式:

  • 运行lerna publish from-git 会将当前的标签中涉及到的NPM包再发布一遍(不会再次更新package.json 只是运行npm publish)
  • 运行lerna publish from-package 会将当前所有本地包中的package.json和远端npm对比,如果npm上不存在此包的最新版本,都会执行一次npm publish

最佳实践

lerna + yarn workspace 结合的Monorepo方案,两者工作职责划分不同:

  • yarn 处理依赖安装工作(只想做好包管理工具)
  • lerna 处理发布流程

yarn管理命令大致分为两类

  • 处理工程下指定的包模块时使用:yarn workspace
  • 处理工程根目录全局或所有包模块时使用:yarn workspaces
    1. yarn install代替npm install. + lerna bootstrap安装工程依赖

它与lerna bootstrap不同的是:

  • yarn install 会将package下的依赖统一安装到根目录下。这有利于提升依赖的安装效率和不同package间的版本复用(有些包需要私有依赖的,而私有以来回被多个包安装多次,而提升依赖可以解决这个问题)
  • yarn install会自动帮助解决安装 包括根目录下的安装 和package link的问题
    1. yarn add [module]
  • 为每个package都按组昂制定的依赖
yarn workspaces add react
  • 为制定的package安装特定依赖:
yarn workspace package1 add react react-dom --save
  • 添加依赖到根目录node_modules中
yar nadd @babel/core -D -W(w 表示依赖天假到workspace根目录下
  • package之间的相互依赖(会在package/package.json下添加依赖)
yarn workspace package1 add package2
  • 在工程目录下引入packages/package包
yarn add package@1.x.x -W
  • yarn remove [module] 和add命令格式一样。
  • 运行工程根目录下的script
yarn test
  • 运行指定包模块下的script
yarn workspace package1 run test
  • 运行所有package下的script命令
yarn workspaces run test