优化多项目管理:yarn+lerna和pnpm的monorepo方案记录

3,969 阅读7分钟

monorepo

介绍

-在multirepo中,每个项目都有自己独立的代码仓库、开发流程、发布和部署过程;而在monorepo中,多个项目则被统一放置在同一个代码仓库中进行管理,共享依赖、工具和代码库。目前,许多著名的开源项目,如Vue3、Babel、React等都采用了monorepo的方式进行代码管理。

image.png

monorepo

优势

  1. 提高代码复用:支持跨项目共享,公共部分以包的形式共享
  2. 解决了需要私服发布和管理npm包
  3. 代码管理更加方便:所有项目都在一个git仓库中
  4. 唯一依赖源:每个依赖只有一个版本,意味着没有版本冲突,没有依赖地域

缺点

  1. 代码库规模越来越大,影响存储
  2. 权限管理,授权后拥有所有子项目的权限
  3. 学习成本:如果代码库包含了许多紧密耦合的项目,那么新成员的学习曲线会更陡峭

在选择 monorepomultirepo 方案时,需要考虑项目的规模和复杂度,以及团队的开发流程和协作方式等因素。如果项目规模比较小或者团队比较紧密,可以考虑选择 monorepo 方案;如果项目规模较大或者团队比较分散,可以考虑选择 multirepo 方案。


使用方式

  • yarn+lerna配套使用,lerna是专门管理的工具(lerna已宣布停止维护)

  • pnpm天生支持monorepo模式,(后起之秀)

比较

yarn+lerna

缺点:lerna已经不在更新,等一些问题

优点:可以解决依赖冲突问题,例如不同项目使用的依赖版本不同

pnpm

缺点:

  1. 依赖版本不同时会造成版本冲突问题,需要统一依赖版本,依赖差异较大的可能不太适合。

优点:

  1. 安装速度最快(非扁平的包结构,没有yarn/npm的复杂的扁平算法,且只更新变化的文件)

  2. 节省磁盘空间 (统一安装包到磁盘的某个位置,项目中的node_modules通过hard-link的方式链接到实际的安装地址)原生支持monorepo,无需第三方工具

monorepo项目结构

最简单的结构

monorepo-project
├─ packages  # 工作区-项目文件夹
│  ├─ project1  # 项目1
│  │  ├─src
│  │  ├─static
│  │  ├─index.html
│  │  ├─.babelrc   # 配置文件
│  │  └─package.json  # 项目依赖和脚本配置文件
│  └─ project2    # 项目2
│  
├─ package.json # 项目依赖和脚本配置文件
└─ pnpm-workspace.yaml  # pnpm的monorepo配置文件

image.png

使用yarn和lerna

采用yarn包管理工具及它的workspace功能。加上lerna工具来配套使用

首先需要安装yarn,lerna

npm install -g yarn lerna

初始化项目

lerna init   # 固定模式,所有包公用一个版本号
lerna init --independent # 独立模式,每个包独立版本号

配置package.json

{
  "workspaces": ["packages/*"] // packages目录下每个子目录作为一个workspace存在
}

创建子项目

cd packages # 进入项目存放的目录
mkdir pkg1  #  创建子项目的文件
cd pkg1 # 进入子项目文件夹
npm init # 初始化项目package.json

一些命令

安装全部依赖

yarn install   # 安装所有的项目依赖

安包

yarn add vconsole  # 安装到根目录,所有子项目都可以调用
yarn workspace 项目名称  add vconsole # 给指定某一个项目安装依赖

卸载依赖

yarn workspace 项目名称 remove vconsole # 卸载指定项目的指定依赖

安装本地依赖

yarn workspace 项目名称 link  # 先将本地依赖链接到全局
yarn workspace 项目名称 add ./packages/pkg3  # 将某本地依赖安装到其他项目

运行

yarn workspace 项目名称  run  dev  # 执行 dev命令

清理node_modules

lerna clean # 清理每个package下面的node_modules

使用pnpm

pnpm可以节约磁盘空间并提升安装速度,比如之前安装过的包,会统一存放到一个公共的地方,再次在别的项目安装时候会进行一个软链接,这样就节约了磁盘空间

  1. 安装速度最快(非扁平的包结构,没有yarn/npm的复杂的扁平算法,且只更新变化的文件)
  2. 节省磁盘空间 (统一安装包到磁盘的某个位置,项目中的node_modules通过hard-link的方式链接到实际的安装地址)
  3. 原生支持monorepo,无需第三方工具

pnpm内置了对单一存储库的支持,可以创建一个workspace来将多个项目合并到一个仓库中。

官方文档pnpm.io/zh/workspac…

项目结构

monorepo
--utils   // 所有子项目都可以通过包形式引入本utils的方法
--workspace
  --web-pc  // 子项目1
  --web-h5   // 子项目2

创建一个monorepo项目

  • 初始化仓库
pnpm init
  • pacakge.json脚本设置

删除一些无用的参数,并添加一些脚本命令

{
  "name": "teacher-apph5-plus",
  "version": "1.0.0",
  "private": true,  // 设置为私有,禁止public
  "scripts": {
    "bootstrap": "pnpm install",
    "preinstall": "npx only-allow pnpm", // 在执行install之前 检查是否用的pnpm工具,不是就不让安装
    "clean": "pnpm recursive exec -- rm -rf node_modules && rm -rf node_modules" // 清理npm包
  }
}
  • 配置pnpm工作区workspaces

根目录创建pnpm-workspace.yaml文件,定义工作区,例如packages 文件夹

官网介绍:pnpm.io/zh/workspac…

packages:
- 'apps/*'
- 'packages/*'
- 'utils/**'
  • 新建.npmrc文件

在根目录下新建.npmrc,增加以下内容

shamefully-hoist=true
strict-peer-dependencies=false
ignore-workspace-root-check=true

shamefully-hoist 是否提升依赖,如果某些工具仅在根目录的node_modules时才有效,可以将shamefully-hoist设置为true来提升那些不在根目录的node_modules,就是将你安装的依赖包的依赖包的依赖包的...都放到同一级别(扁平化)。说白了就是不设置为true有些包就有可能会出问题。

  • 创建utils项目和工作区

创建utils文件夹,初始化,然后创建index.js入口文件

cd utils
pnpm init  # 初始化package.json

创建完毕后,可以在package.json中将项目名称改成@xls/utils 防止名称冲突

index.js 入口文件

function sayHi(){
  console.log("hello l is linyi")
}
export {
	sayHi
}
  • 创建packages工作区文件夹
# 在该文件夹内创建子项目 web-pc   web-h5
cd packages
npm init vue@3

在子项目中安装本地依赖utils

# 进入子项目文件夹
pnpm add @xls/utils

在main.js中引入使用

// .........
import { sayHi } from '@xls/utils'
sayHi() // 调用
// .........

公共的包不需要打包,因为如下

这里因为不是直接发布到npm的,而是作为依赖放到项目中使用,在你编译项目时会自动跟随项目编译的

全局依赖

在仓库根目录安装,之后即可再所有子项目中使用

pnpm add vue  # 在仓库根目录下安装,在所有的字项目中都可以使用

一些命令

// 在仓库根目录下

pnpm install   // 安装全部依赖

pnpm recursive exec -- rm -rf node_modules && rm -rf node_modules // 删除所有依赖

启动子项目
pnpm --filter answerdata run dev
pnpm --filter errorbook run dev
安装在项目根目录中的依赖,在所以子项目中可以直接使用

启动全部
pnpm run --filter '*' dev

子项目中安装 本地其它公共包
pnpm --filter vue-demo1 add utils

或者可以进入子项目目录中安装
pnpm add utils

打包子项目
pnpm --filter errorbook run build

最后

yarn+lerna或pnpm

选择yarn+lernapnpm的monorepo工具应该根据你的具体需求和团队的技术栈进行评估。以下是一些参考因素:

  1. 团队技术水平:yarn和lerna的学习曲线较陡峭,而PNPM的学习曲线较为平缓。因此,如果团队中有很多新手,pnpm可能是更好的选择。
  2. 项目复杂度:如果你的项目结构非常复杂,需要处理很多依赖关系和版本冲突,那么lerna的功能可能更适合你的需求。
  3. 性能需求:如果你关注monorepo工具的性能表现,pnpm可能是更好的选择。pnpm使用了一种名为“虚拟包管理器”的技术,可以显著减少依赖的安装时间和磁盘空间。

新项目推荐使用pnpm