lerna的多包管理实践与使用| 8月更文挑战

2,035 阅读6分钟

参考(这几篇文章很重要,一定要看)
lerna(中文)
现代前端工程化-彻底搞懂基于 Monorepo 的 lerna 模块(从原理到实战)
Node.js 如何创建软链接,与硬链接有什么区别?

我们的项目用到了lerna管理包,在做了大概1年多的项目之后,重新用lerna来管理,为什么突然间要用呢?

因为我们有项目A(vue项目),对应后端的项目B(node.js),这两个项目都是对公司内部的,这两个项目里面有很多共同的utils,假设在项目A要找一个接口,并且要去看接口对应项目B的后端代码时,我们要打开两个项目,然而这些似乎不是很大的一个问题

后来要开发一个新的后端管理系统,给b端客户看,并且这个系统与我们A.B项目在接口上有很多需要互通的, ,对应项目项目C(vue项目),对应后端的项目D(node.js),这个时候就提出来了lerna管理包,因为A和c使用的包几乎一致,并且之前有在A上封装了不少项目内部的npm包发布到私服.B,D也是一致的,这时候 我们要解决的问题应该是,也是lerna能够解决的问题

1.同样的包不应该每个项目都下载,这样太占浪费了大量存储空间 
2.私服的包虽然稳定,但是并不是一成不变的,如何调试更简单
3.相互联系的项目如何减少联调时间 
4.一个项目依赖了多个 npm 包,当某一个子 npm 包代码修改升级时,都要对主干项目包进行升级修改。
(这个问题感觉是最烦的,可能一个版本号就要去更新一下代码并发布)

1.如何将已经存在的项目用lerna管理并运行

假设我有一个用vue-cli新建的project01项目(已存在)

1.1 新建lerna项目
mkdir lerna-repo && cd $_
npx lerna init
1.2 新建package
lerna create project01

将project01里所有的文件(除了package.json)放到packages/project01底下,再就将package.json内容复制到packages/project01/package.json里面,保留有用的信息

1.3 配置lerna.json文件以及package.json文件

yarn workspaces ,可以解决前面说的当不同的项目依赖不同的版本号问题, yarn workspaces会检查每个子项目里面依赖及其版本,如果版本不一致都会保留到自己的 node_modules 中,只有依赖版本号一致的时候才会提升到顶层。注意:这种需要在 lerna.json 中增加配置。

{
  "packages": [
    "packages/*"
  ],
  "npmClient": "yarn",
  "useWorkspaces": true,
  "version": "independent"
}

 顶层的 package.json配置

 "workspaces":[
      "packages/*"
  ],

注意lerna默认使用的是集中版本,所有的package共用一个version,如果需要packages下不同的模块 使用不同的版本号,需要配置Independent模式。命令行介绍时有提到这里 在json 中增加属性配置

如果是成熟的项目,合并到一起,用到了同一个包,不用的版本,可以采用Independent模式,我这里也用这个,因为我觉得这样更加灵活

1.4 yarn install

可以在lerna-repo地下进行安装,也可以在project底下进行安装

1.5 运行
cd packages
cd project01
npm run serve

1627820314(1).png

2.如何在最顶级目录运行子项目

2.1在顶级的package.json里面配置
 "scripts": {
    "project01-dev": "lerna exec --scope project01 -- yarn serve"
  }

不同的环境应该配置多个project01-XXX project01表示是packages地下的项目名,serve对应的是project01底下package.json里面的scripts项

1627820892(1).png

2.2运行
npm run project01-dev
3. 如何调试私服的npm 项目

1627821888(1).png

1627821214(1).png

lerna create bingxixi-private-common-utils

将bingxixi-private-common-utils里所有的文件(除了package.json)放到packages/bingxixi-private-common-utils底下,再就将package.json内容复制到packages/bingxixi-private-common-utils/package.json里面,保留有用的信息, 一定要注意main的指向是对的.

1627821466(1).png

在bingxixi-private-common-utils的文件夹上加上@号进行软链

yarn install

在project01的main.js里面引入

import { userPrivate, getNumBit } from 'bingxixi-private-common-utils'
Vue.prototype.$userPrivate = userPrivate
Vue.prototype.$getNumBit = getNumBit

在修改@bingxixi-private-common-utils里面的信息时,project01呈现的页面也会改变

1627821980(1).png

4.测试2个相似的项目,相同的包会被提到最顶级的node_modules

在project01和project02都没有进行install的情况下,project02和project01的内容完全一致,不管是在任一个package里面运行以下,都会有公共的包会提到顶级的node_modules里面

以上解决了私服的包虽然稳定,但是并不是一成不变的,如何调试更简单

yarn install

你好呀9.gif

这个也就是他主要的作用之一,解决了我们项目同样的包不应该每个项目都下载,这样太占浪费了大量存储空间

5.lerna clean

957714b62863bac91c04b63622accb2.png

b8a54e9b9c070c7fe872d5ab4704a70.png

执行lerna clean,只会删除packages地下的项目的node_modules,并不会对最顶级的node_modules进行删除

6.测试同一个包,不同的版本
6.1错误的示范

1627874013(1).png

bingxixi-common-title有7个版本

假设我的project01的package.json里面

1627874142(1).png

假设我的project02的package.json里面

1627874176(1).png

yarn install 之后

1627874291(1).png

project01和project02里面都找不到bingxixi-common-title,而顶级的node_modules里面居然是"version": "1.0.7",这似乎与我们的猜想完全不符,是因为这里不应该加上^

1627874419(1).png

表示安装大版本的最高中版本: ^版本,比如 "antd": "^3.1.4",
表示安装3.1.4及以上的版本,但是不安装4.0.0,也就是说安装时不改变大版本号。

意思就是平时我们用一个包,都说的n点m版本的,也许有比n更大的,也许有比n更小的,
而加上^就会安装n点max的版本,所以如果加上^我们测试不出来不同版本下lerna如何处理
6.2 正经测试(去掉上面提到的^符号)

1.先去掉上面提到的^符号 2.一定要进行lerna clean,不然测试出来的效果可能会乱

lerna clean --y

3.安装

yarn install 

4.结果

顶级node_modules

1627874993(1).png

project01的node_modules里面并没有bingxixi-common-title包

而project02的node_modules里面

1627875177(1).png

在功能上确实是符合:所有的package共用一个version,如果需要packages下不同的模块 使用不同的版本号,需要配置Independent模式

经过多次测试,发现顶级的node_modules一直都是package.json里面最低版本的,而其他的稍高版本的会放到对应的项目模块底下,根据node的文件查询规则,不同的项目同一个包不同版本会被保留,按照最初的版本使用

3 关于independent模式的思考(希望大家来指正)

参考: lerna多包管理实践

1627889202(1).png

lerna默认使用的是集中版本,所有的package共用一个version,如果需要packages下不同的模块 使用不同的版本号,需要配置Independent模式。

固定模式  --exact
固定模式,通过lerna.json的版本进行版本管理。当你执行lerna publish命令时, 
如果距离上次发布只修改了一个模块,将会更新对应模块的版本到新的版本号,然后你可以只发布修改的库。

这种模式也是Babel使用的方式。如果你希望所有的版本一起变更, 可以更新minor版本号,
这样会导致所有的模块都更新版本。

独立模式 --independent
独立模式,init的时候需要设置选项 --independent. 独立模式允许管理者对每个库单独改变版本号,
每次发布的时候,你需要为每个改动的库指定版本号。这种情况下, lerna.json的版本号不会变化了, 
默认为independent。

意思就是采用固定模式,任何一处改动,所有packages里面的项目的version号都会变,而且是一致的.
采用independent是packages里面的项目的version号只有在各自项目变化的时候才更新,我的理解就是:如果需要各个组件维护自身的版本号,那么就使用independent模式,假设你的私包作为你的npm包,开始是1.0.0版本,后续需要变化才顺序更新,可以采用independent

4.使用lerna的一点感悟(相比之前)

我们用lerna主要是为了节省了大量存储空间,如果统一私服的包管理,可以使用lerna publish,所以我们项目种并没有用到包发布这块

优点:

1. 节省了内存空间,比如常见了vue,element ui,egg等包,不用重复下载
2. 前后端联调更加简单,搜索接口,能快速找到对应的部分(以一个项目整体来看)
3. 比如搜索关键词,几个项目可能有很多相同,又可以单独运行来搜索
4. 修复bug的时候,可以多个项目一起修改,审核mr也是只用去看那一个项目
5. 减少代码的复用,可以将公共部分提取出来
6. 与npm私包软链更简单

缺点:

1.我们后面将6.7个项目都放到lerna管理,刚开始合并的过程是需要花费不少时间的,我所列举的是一小部分
2.yarn install还是要很长时间的