Monorepo 和 Multirepo

单一仓库(Monorepo)架构,可以理解为:利用单一仓库来管理多个packages的一种策略或手段;与其相对的是多仓库(Multirepo)架构

Monorepo 目录中除了会有公共的package.json依赖以外,在每个sub-package子包下面,也会有其特有的package.json依赖。

兄弟模块之间可以通过模块 package.json 定义的 name 相互引用,保证模块之间的独立性

# monorepo目录结构
monorepo-demo
├── packages
│ ├─ module-a
│ │ ├─ src # 模块 a 的源码
│ │ ├─ node_modules # 模块 a 的 node_modules
│ │ └─ package.json # 仅模块 a 的依赖
│ └─ module-b
│ ├─ src # 模块 b 的源码
│ └─ package.json # 仅模块 b 的依赖
├── .eslintrc # 配置文件,对整个项目生效
├── node_modules # 所有子包公共的 node_modules
└── package.json # 所有子包公共的依赖

Multirepo 更倾向与在项目制中,将一个个项目使用不同的仓库进行隔离,每一个项目下使用独有的package.json来管理依赖

# multirepo-a目录结构
multirepo-a
├── src
├── .eslintrc
├── node_modules
└── package.json # multirepo-b目录结构
multirepo-b
├── src
├── .eslintrc
├── node_modules
└── package.json

Monorepo 工具

在采用 Monorepo(单一仓库)架构的软件开发中,工具的选择是至关重要的。合适的 Monorepo 工具能够帮助团队更高效地管理大规模代码库、提升协同开发体验以及优化构建和部署流程。

直至 2024 年,目前在前端界比较流行的 Monorepo 工具有 Pnpm WorkspacesYarn Workspacesnpm WorkspacesRush

TurborepoLernaYalc、和 Nx

强烈推荐使用Pnpm Workspaces 作为 Monorepo 项目的依赖管理工具

那么 Monorepo 与包管理工具(npm、yarn、pnpm)之间是一种怎样的关系?

这些包管理工具与 monorepo 的关系在于,它们可以为 monorepo 提供依赖安装与依赖管理的支持,借助自身对 workspace 的支持,允许在 monorepo 中的不同子项目之间共享依赖项,并提供一种管理这些共享依赖项的方式,这可以简化依赖项管理和构建过程,并提高开发效率。

Monorepo 项目搭建

背景

传统的多仓库 Multirepo 模式,通常都是一个仓库存放一个项目。比如现在你有三个项目,就需要创建三个远程仓库,并且需要为每个项目单独安装和升级依赖

而单一仓库 Monorepo 模式,就是在一个仓库中管理多个项目,这些项目可以是独立的,也可以相互依赖。通过 Monorepo,多个项目可以共享依赖。比如多个项目都需要 lodash,那我们也只需安装一次即可

pnpm i lodash -w

当然,Monorepo 中除了公共的package.json依赖以外,在每个sub-package子包下面,也会有其私有的package.json依赖

我们本次选择Pnpm Workspaces 作为 Monorepo 项目的依赖管理工具,一起来搭建一个 monorepo 项目

安装包管理工具

全局安装 pnpm

npm i pnpm -g

初始化项目

创建一个新的项目目录 pnpm-monorepo,根目录运行 pnpm init 创建 package.json 文件

然后根目录新建一个文件夹 packages,用于存储子包

新建 packages/libc-shared( 共享包 ),用于存放多个项目或组件之间共享的代码 。运行 pnpm init 创建 package.json 文件,修改 package.json 的 name 为 "@libc/shared";修改 package.json 的 main 入口文件路径字段为"src/index.js"

{
"name": "@libc/shared",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
}

新建 packages/libc-ui( 公共组件包 ),即UI组件库,这里我们直接 clone 了 iview-ui-plus 代码。运行 pnpm install 安装依赖,

修改 package.json 的 name 为 "@libc/ui";修改 package.json 的 main 入口文件路径字段为"src/index.js"

然后我们在 packages 下创建两个 vue 项目,vue-dom1vue-dom2,运行脚本pnpm create vue@latest。由于两个项目的依赖是完全一样的,我们可以将 dependencies、devDependencies 复制到外层 package.json 中当做公共依赖,然后pnpm install 安装一次即可

到了这一步,vue 项目还是不能运行,必须要先配置 workspace,用于支持多包存储库让子包 vue 项目可以访问到我们的公共依赖

配置workspace

根目录新建一个 pnpm-workspace.yaml,将 packages 下所有的目录都作为包进行管理

packages:
# all packages in direct subdirs of packages/
- 'packages/*'

pnpm-monorepo 最终项目结构

pnpm-monorepo/
├── packages/
│ ├── libc-shared/
│ ├── libc-ui/
│ ├── vue-dome1/
│ └── vue-dome2/
├── package.json
└── pnpm-workspace.yaml

子包共享

此时,pnpm-workspace.yaml工作空间下的每个子包都可以共享我们的公共依赖了。还有个问题是,兄弟模块之间如何共享呢?

之前我们说过,子包之间可以通过 package.json 定义的 name 相互引用,一起看下两个实际场景

  1. 如何把子包 libc-shared 共享出去?

--workspace参数去安装共享子包,会去 workspace工作空间中找依赖项并安装

pnpm install @libc/shared --workspace -w

package.json 中就会自动添加如下依赖,"workspace:" 只会解析本地 workspace 包含的 package

"dependencies": {
"@libc/shared": "workspace:^"
}

此时,vue 项目就可以使用公共包 libc-shared 里的方法,import 引入即可

import { isObject } from '@libc/shared'
  1. 如何把子包 libc-ui 共享出去?

重复一下上面的步骤,然后我们去引用一个 button组件,发现报错了 Failed to resolve import "./base" from "../libc-ui/src/components/typography/title.vue". Does the file exist?

vite.config.js 中添加 extensions 即可解决,配置一下省略的扩展名列表

resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}

虽然 button 组件引用成功了,但是发现没有任何样式效果。在 libc-ui/src/index.js 文件中导入一下样式文件就行了

import "./styles/index.less";

依赖

公共依赖

全局安装公共依赖 lodash。需要加-w(在工作空间的根目录中启动 pnpm)

pnpm install lodash -w

这样,vue-dom1 和 vue-dom2 这两个 vue项目就都可以使用 lodash 库了

局部依赖

如果只有 vue-dom1 项目用到了 lodash,我们也可以安装到 vue-dom1 项目内部,不作为公共依赖项,有两种方法可以实现

  1. cd 到 src/packages/vue-dom1 目录下,直接安装
pnpm install lodash
  1. 在任意目录下,使用 --filter 参数进行安装;package_selector:package.json 对应的 name 字段
pnpm install lodash --filter <package_selector>

shamefully-hoist

shamefully-hoist,默认 false

  • false:node_modules下只能看到直接依赖的套件,次级依赖在node_modules/.pnpm 目录下;无法访问其他子包局部安装的依赖项,例如,vue-dome2 安装的 lodash,vue-dome1 是访问不到的

  • true:將所有套件都拉升到 node_modules 目錄下,能访问到其他子包局部安装的依赖项,例如,vue-dome2 安装的 lodash,vue-dome1 是能访问到的

// .npmrc

# pnpm 配置
shamefully-hoist=false

配套代码

GitHub - burc-li/pnpm-monorepo: vue3 + pnpm + monorepo 项目 demo

参考文档

为什么 pnpm+monorepo 是组件库项目的最佳实践

突破项目瓶颈:2024 年 Monorepo 工具选择和实践 | BEEZEN

GitHub - Tyh2001/vue3-pnpm-monorepo: vue3 + pnpm + monorepo 项目 demo

vue3 + pnpm 打造一个 monorepo 项目的更多相关文章

  1. 从0搭建Vue3组件库(二):Monorepo项目搭建

    本篇文章是从0搭建Vue3组件库系列文章第二篇,本篇文章将带领大家使用pnpm搭建一个简单的Monorepo项目,并完成包的关联与测试 什么是 Monorepo 其实很简单,就是一个代码库里包含很多的 ...

  2. 【源码项目+解析】C语言/C++开发,打造一个小项目扫雷小游戏!

    一直说写个几百行的小项目,于是我写了一个控制台的扫雷,没有想到精简完了代码才200行左右,不过考虑到这是我精简过后的,浓缩才是精华嘛,我就发出来大家一起学习啦,看到程序跑起来能玩,感觉还是蛮有成就感的 ...

  3. 【前端vue进阶实战】:从零打造一个流程图、拓扑图项目【Nuxt.js + Element + Vuex】 (一)

    本系列教程是用Vue.js + Nuxt.js + Element + Vuex + 开源js绘图库,打造一个属于自己的在线绘图软件,最终效果:topology.le5le.com .如果你觉得好,欢 ...

  4. 打造一个高逼格的android开源项目——小白全攻略 (转)

    转自:打造一个高逼格的android开源项目 小引子 在平时的开发过程中,我们经常会查阅很多的资料,最常参考的是 github 的开源项目.通常在项目的主页面能看到项目的简介和基本使用,并且时不时能看 ...

  5. 如何打造一个"逼格"的web前端项目

    最近利用空余的时间(坐公交车看教程视频),重新了解了前后端分离,前端工程化等概念学习,思考如何打造一个“逼格”的web前端项目. 前端准备篇 前端代码规范:制定前端开发代码规范文档. PS:重中之中, ...

  6. 微人事 star 数超 10k,如何打造一个 star 数超 10k 的开源项目

    看了下,微人事(https://github.com/lenve/vhr)项目 star 数超 10k 啦,松哥第一个 star 数过万的开源项目就这样诞生了. 两年前差不多就是现在这个时候,松哥所在 ...

  7. 【前端新手也能做大项目】:跟我一起,从零打造一个属于自己的在线Visio项目实战【ReactJS + UmiJS + DvaJS】(二)

    本系列教程是教大家如何根据开源js绘图库,打造一个属于自己的在线绘图软件.当然,也可以看着是这个绘图库的开发教程.如果你觉得好,欢迎点个赞,让我们更有动力去做好! 本系列教程重点介绍如何开发自己的绘图 ...

  8. Qt 新手实战项目之手把手打造一个串口助手

    一前景 很多时候我们在学习一门新的语言,一直在学习各种语法和记住各种关键字,很容易产生枯燥的情绪,感觉学习这些玩意儿不知道用在什么地方,心里很是苦恼,这不,我在这记录下我学习Qt的第一个的小项目-串口 ...

  9. 基于 Lerna 管理 packages 的 Monorepo 项目最佳实践

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/NlOn7er0ixY1HO40dq5Gag作者:孔垂亮 目录 一.背景二.Monorepo vs M ...

  10. [后端人员耍前端系列]AngularJs篇:使用AngularJs打造一个简易权限系统

    一.引言 上一篇博文已经向大家介绍了AngularJS核心的一些知识点,在这篇博文将介绍如何把AngularJs应用到实际项目中.本篇博文将使用AngularJS来打造一个简易的权限管理系统.下面不多 ...

随机推荐

  1. ZEGO 最后一公里网络传输的容灾及优化方案

    作为运维,你是否遇到过一些用户域名解析异常,你是否又遇到过某些区域云商加速节点异常导致业务不可用,此时的你一脸茫然,不知所措?作为运维,你是否被最后一公里问题搞得焦头烂额? 那么今天我们就来探讨一下最 ...

  2. PDF解析,还能做得更好

    随着大模型文档智能应用逐渐步入正轨,文档解析类产品成为其中重要的一环.文档解析工具能够"唤醒"沉睡在PDF文件中的知识,将其转化为机器能够识别.读取的信息,将可用数据从txt.cs ...

  3. Angular 18+ 高级教程 – Signals

    前言 首先,我必须先说明清楚.Signal 目前不是 Angular 的必备知识. 你的项目不使用 Signal 也不会少了条腿,断了胳膊. Angular 官方维护的 UI 组件库 Angular ...

  4. Angular 18+ 高级教程 – 关于本教程

    版本声明 本教程写于 Angular v17,但往后的所有新功能,API 都有更新到相关文章里头,所以教程总是最新的,大家可以安心学习. 前言 光阴飞逝,一转眼,我尽然已经有两年多的时间完全没有接触 ...

  5. 算法与数据结构——AVL树(平衡二叉搜索树)

    AVL树 在"二叉搜索树"章节提到,在多次插入和删除操作后,二叉搜索树可能退化为链表.在这种情况下,所有操作的时间复杂度将从O(logn)劣化为O(n). 如下图,经过两次删除节点 ...

  6. 图书《React.js实战》代码下载

    图书<React.js实战>代码下载链接:https://pan.baidu.com/s/1kep0xsTeSupyr15c3VwmBw 提取码:9pra 这个代码经过图书<Reac ...

  7. foobar2000 v2.1.6 汉化版

    foobar2000 v2.1.6 汉化版 -----------------------[软件截图]---------------------- -----------------------[软件 ...

  8. MYSQL存储过程-练习5 游标

    MYSQL存储过程-练习5 游标 1 DELIMITER $ 2 CREATE PROCEDURE sp_cur() 3 BEGIN 4 DECLARE bkname VARCHAR(200); 5 ...

  9. Android复习(五)设备兼容—>多apk支持

    1. 对于不同的屏幕发布单独的apk https://developer.android.google.cn/training/multiple-apks/screensize 2.多窗口模式 在An ...

  10. 在 Kubernetes Pod 中如何获取客户端的真实 IP

    Kubernetes 依靠 kube-proxy 组件实现 Service 的通信与负载均衡.在这个过程中,由于使用了 SNAT 对源地址进行了转换,导致 Pod 中的服务拿不到真实的客户端 IP 地 ...