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包的管理情况。
总结下,有几点优势:
- 自动解决packages之间的依赖关系
- 通过
git检测文件改动,自动发布 - 根据
git提交记录,自动生成CHANGELOG - issue的集中管理,解决多包库的issue混乱痛点
缺点:
- 当源码足够大的时候,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:** 指定包所在的目录
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。
如何创建工作区?
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