Lerna

123 阅读6分钟

image.png

0.背景

什么是lerna?为什么会出现lerna?它的出现解决了谁带来的哪几个问题? 目前对于前端项目来说一般都是一个业务项目对应一个仓库,如果说遇到多项目共同的抽象逻辑(例如:utils函数、公共组件等),一般采用两种方案:

方案1. 繁琐代码复制的问题
问题1)重复的公用函数带来同样的问题重复维护;
问题2)增加文件体积,延缓构建时间

方案2. 上传npm(通过引用共享公用npm仓库)的问题:
问题1)调试繁琐:上传npm之前,如果本地项目引入这个npm包调试,那么需要先将npm包通过npm link链接到全局,然后业务项目本地启动的时候node_module就可以走本地的npm包了。
问题2)共享的npm包升级问题:如果这个npm包被多个项目所使用,那么也就意味着一个npm包升级,下面使用的多个项目都要进行相应的升级然后发布(如果是前后升级不兼容,那么发布不及时就会导致bug),可以说牵一发而动全身了。

Lerna 的使用场景
针对上述问题可以发现对于多个npm包的管理是非常麻烦的,package之间相互依赖,开发人员需要在本地手动执行npm link,手动维护版本号的更替,发布繁琐, 多个package之间可能存在相互依赖, 多工程切换, 更新版本号。每一个package都包含独立的node_modules,而且大部分都包含babel,webpack等开发时依赖,安装耗时冗余并且占用过多空间。因此,lerna的使用场景就是: 开发UI组件库Util工具库以及插件库这种多个npm包的管理情况。

总结下,有几点优势:

  1. 自动解决packages之间的依赖关系
  2. 通过git 检测文件改动,自动发布
  3. 根据git 提交记录,自动生成CHANGELOG
  4. issue的集中管理,解决多包库的issue混乱痛点

缺点:

  1. 当源码足够大的时候,repo体积较大;

2. 统一构建工具,对构建工具提出了更高要求,要能构建各种相关module

关于依赖管理的原则:www.pipipi.net/21504.html

一、Lerna核心原理

在了解lerna原理之前,我们需要知道先了解一个monorepo的概念,monorepo是一种多项目中共享包管理的实现方案。也就说我们之前的mutirepo是单个项目单个仓库,这个仓库中的node_module是内部文件/组件级别获取共享资源包的地方(为了避免同一个项目中多个文件引入共同文件而避免复制的问题)。但是如果提升到项目级别来看node_module包的话,那么又会存在多个项目引入共同文件,同样会带来复制问题 & 管理维护问题 & 多项目包升级问题。那么如果我们提升一个级别,从 文件级别提升 到 项目级别,也就是说一个仓库存放多个项目,那么这个仓库中的node_module是服务于这下面多个项目的。那么这就是monorepo的多项目包管理的核心思想。
(注意: 如果一个npm包只能给一个项目使用那么就不存在升级问题,但是你既然提升到npm包中就一定是为了多项目服务的,所以升级一定会牵扯到其他项目的改动)。

二、Lerna的基本使用

这里简单提一下安装的问题:npm 安装的时候出现一个堆栈溢出的问题,这里使用的方法是清除npm中的cache缓存:npm cache clean --force,这些缓存就是从远端仓库下载后的压缩包,当我们npm install下载的时候下载过的包会放在压缩到cache中,然后直接解压到node_module文件中形成一个新的解压文件。

step1: 在项目外层的仓库下执行lerna init来对总仓库进行初始化 lerna init
step2: 在该项目下创建两个项目 【项目one】 && 【项目two】:lerna create one && lerna create two,得到如下文件:

.
├── lerna.json
├── package.json
└── packages
    ├── one  // 项目 one
    │   ├── __tests__
    │   ├── lib
    │   ├── README.md
    │   └── package.json
    └── two  // 项目 two
        ├── __tests__
        ├── lib
        ├── README.md
        └── package.json

2.1 lerna.json 文件解析

lerna中对于总仓库的版本控制有两种模式:
a)一种是Fixed默认模式-即所有的项目和总项目是同步更新版本的,一开始lerna的version是0.0.0,且所有的package子项目下的version也都是0.0.0, 并且如果一个子项目有更改重新发布更新,那么总项目和子项目都会同步更新为0.0.1。
b)一种是independent独立模式。这种模式下lerna的version字段值为‘independent’,而各个子项目中的如果某一个有新的版本,那么其他子项目的版本不会随之变动。

// lerna.json
{
  "packages": [
    "components/*"
  ],
  "version": "independent",
  "npmClient": "yarn",
  "useWorkspaces": true,
  "command": {
    "bootstrap": {
      "ignore": "",
      "npmClientArgs": ""
    },
    "publish": {
      "ignoreChanges": [
        "ignored-file",
        "*.md"
      ]
    }
  }
}
**useWorkspaces**: 是否使用workspace来管理依赖  
**npmClient:**  允许指定命令使用的client, 默认是 npm, 可以设置成 yarn  
**command.publish.ignoreChanges:** 可以指定那些目录或者文件的变更不会被publish  
**command.bootstrap.ignore:** 指定不受 bootstrap 命令影响的包  
**command.bootstrap.npmClientArgs:** 指定默认传给 lerna bootstrap 命令的参数  
**command.bootstrap.scope:** 指定那些包会受 lerna bootstrap 命令影响  
**packages:** 指定包所在的目录

image.png

2.2 package.json中的workspaces

参考文献:blog.csdn.net/weixin_4468…

2.3 lerna 对依赖包的下载

lerna add来安装依赖,默认情况下它会利用npm install来执行我们的安装命令

2.4 核心:Lerna Hoisting原理

2.4.1. lerna.json 中的 useWorkspace 和 package.json 中 的workspaces

lerna.json中的useWorkspace就是相当于需要设置package.json 中 的workspaces字段,那么workspaces字段的作用是什么呢?这个字段很有作用,可以替代npm包本地调试的npm link的。

package.json中的workspaces字段的作用
workspaces字段是用来管理项目中的工作区的,那么什么是工作区呢?即文件夹内部包含package.json文件的即为工作区。如下图所示:存在3个工作区(workspace),分别是bigProject、smallProject1、smallProject2。 image.png 如何创建工作区?
npm init -w ./package/smallProject1。这样,我们就在 bigProject 大的工作目录下创建了2个小的workspace。 workspaces原理
和npm link相同,也是基于node文件查找方式,先从本地工作区向上查找,然后通过到 bigProject的node_module,这个时候node_module会通过软连接到对应的子项目内的node_module中。

2.5 Lerna中如何构建发布

对于构建,因为多个项目是在一个仓库里,那么如果我只改动了一个项目,那么构建的时候需要将另外的项目也一起构建吗,如果是这样的话那么构架时间是不是太久了。 这里的构建就是打包,这里的打包是通过rollup打包

发布:是只发布改动的项目,还是所有项目一起发布呢

参考文献: blog.csdn.net/qq_36943808…

demo实例参考: zhuanlan.zhihu.com/p/404166248

优雅发包:juejin.cn/post/701262…