天冷了,来碗monorepo一锅炖吧

小明同学在做项目的时候遇到了一个问题🤔🤔,用pnpm搭建的monorepo下有三个项目,分别是a、b和sdk,项目a和项目b都依赖sdk,按照monorepo的文档说法,sdk应该提升到根目录的node_modules,但是小明发现自己在根目录的node_modules下根本没找到sdk的影子😮😮这是怎么回事,那到底什么才是monorepo,这个问题显然冲击了小明对monorepo浅薄的理解🤢🤢

于是,小明同学开始狠狠恶补相关概念,势必成为monorepo糕手🕶

什么是monorepo

monorepo是一种项目代码管理方式 ,单个仓库可包含许多相关但独立的项目,可以由同一个团队或多个团队管理。

monorepo(单仓库)和multirepo(多仓库)的区别

multirepo又称为polyrepo,就是每一个package单独用一个仓库进行管理,而monorepo将所有package都放在一个仓库进行管理。

好处

一致性:由于所有项目都存储在同一个存储库中,执行代码质量标准和统一风格会更容易。

轻松的补丁管理:由于所有共享依赖项都集中在一个地方,我们可以快速升级所有项目版本。这为我们节省了大量时间,使我们能够进行更令人兴奋的工作。

缺点

冗长的CI/CD流程:所有项目都在同一个存储库中,有的时候需要构建和测试不必要的没有发生任何改变的项目,会导致代码到生产的时间延长。

小明同学:🤔这我也知道monorepo的思想啊,可是一提到软链接和workspace就阴暗爬行了🤢

那这就不得不提到pnpm

什么是pnpm

performance npm(高性能的npm),官网对它的描述是高速的,节省磁盘空间的包管理工具

好处

节省磁盘空间: 统一安装包到磁盘的某个位置,项目中的node_modules通过hard-link的方式链接到实际的安装地址

**提高安装速度:**官网提供的不同包管理在不同场景下的耗时对比如下图所示,原因看链接

项目初衷 | pnpm

创建了一个非扁平的node_modules目录:

扁平结构是安装一个包,这个包所依赖的包将一起被安装到同一级目录下。小明同学运行了一下,仅仅<font style="color:rgb(28, 30, 33);">npm install axios</font>,展示在node_modules里显得非常多,这是因为axios需要的依赖例如<font style="color:rgb(28, 30, 33);">form-data</font>也被塞到和axios同级目录了。

而换了pnpm,axios需要用到的依赖被塞进了.pnpm文件夹,所以就显得格外简洁了。

当然这些只是表面的东西,除了外观上不同,扁平化和非扁平化还是存在着本质的差异。

扁平结构存在的问题

①扁平化算法复杂,耗时

②依赖本身结构具有不确定性

.pnpm

.pnpm文件夹被称为虚拟存储目录,以平铺的形式储存着所有的包,像上文中有白色特殊标记的axios👉只是一个符号链接,当node.js解析依赖的时候,它使用的依赖的真实位置在node_modules/.pnpm/目录下。pnpm结合了软链和平铺目录去实现构建一个嵌套的结构。

Workspace

pnpm 内置了对单一存储库(也称为多包存储库、多项目存储库或单体存储库)的支持, 你可以创建一个 workspace 以将多个项目合并到一个仓库中。

要想启动pnpmworkspace功能,需要工程根目录下存在 配置文件,并且在pnpm-workspace.yaml中指定工作空间的目录。

jsx 复制代码
packages:
  # specify a package in a direct subdir of the root
  - 'my-app'
  # all packages in direct subdirs of packages/
  - 'packages/*'
  # all packages in subdirs of components/
  - 'components/**'
  # exclude packages that are inside test directories
  - '!**/test/**'

在设置依赖版本的时候推荐用workspace:*,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本。当pnpm publish的时候,会自动将package.json中的workspace修正为对应的版本号。

常用monorepo pnpm命令

列出sdk的源码位置,被monorepo内部哪些项目引用了

json 复制代码
pnpm why sdk

取消某个依赖的安装

json 复制代码
//整体移除
pnpm remove axios
//局部移除
pnpm remove axios --filter @monorepo/a

常用配置

小明同学渐入佳境,感觉自己离问题的答案越来越近了,于是他在官网找到了如下依赖提升的配置

hoist

  • 默认值为true
  • 当值为true的时候,所有的依赖会被提升到node_modules/.pnpm/node_modules,使得node_modules内的所有包都可以访问未列出的依赖项

shamefully-hosit

  • 译为丢脸的提升(不是),默认值为false
  • 会造成破坏隔离性 的问题:使用shamefully-hosit后,pnpm的隔离优势会被部分削弱,可能引入与npm扁平化依赖相同的问题

hoist-pattern

  • 默认值:['*']
  • 告诉pnpm哪些包应该被提升到node_modules/.pnpm/node_modules默认情况下,所有包都被提升

public-hoist-pattern

  • 默认值:['eslint ','prettier']
  • 不同于hoist-pattern一样提升到虚拟存储目录,public-hoist-pattern将匹配的依赖提升至跟模块目录中。

学到这里,小明同学虽然还不知道自己做项目出现问题的原因在哪里,但他已经学会解决了!既然没有提升到根目录,那我就想方设法将共有依赖sdk提升到根目录不就对了?

于是小明同学写下了这段"羞耻"的代码:

yaml 复制代码
packages:
  - "packages/*"
shamefully-hoist: true

这下好了,sdk是顺利找到了,根目录node\_modules里面瞬间"活跃"起来了😱😱

小明同学:自己小钱包里的钱要是也能这样翻倍就好了😋(不是

这样的处理方式显然不太符合预期和小明对项目设计的要求以及pnpm本身的隔离特点,如果只是提升sdk这个特殊的依赖该多好呀🤔

问题是解决了,但小明同学心中有一个小小的问题:hoist默认值为true了,为什么提升sdk还需要这么费劲?按理说,monorepo不应该会将所有的公用依赖提升到根目录node\_modules嘛,难道是自己的小电脑坏了😰😰

按照小明同学的思路,我们在项目a和项目b都安装了lodash依赖,可以看出monorepo确实是提升到了根目录

小明同学:坏了,我的sdk被孤立了,一定是这样😭😭

这里就不得不再说一说workspace了,当一个依赖是子项目的时候,pnpm会优先将它作为软链接分发给其他子项目。在a和b的package.json中分别加入如下代码:

json 复制代码
{
  "dependencies": {
    "sdk": "workspace:*"
  }
}

运行pnm install时,并不会将sdk提升到根目录的node_modules中,而是为每个依赖它的项目创建一个指向packages/sdk的符号链接。

还有像这样不会被提升的特殊情况嘛?有的。

  1. 公共依赖版本必须兼容
  • 如果多个子项目依赖同一个包,但版本号不一致,包管理工具不会将该包提升到根目录,而是保留在各自子项目的node_modules下。
  1. 如果某些依赖被声明为 peerDependencies(而非 dependenciesdevDependencies),它们不会被提升。

学习了上面的内容,已经顺利解决了问题,但出于对monorepo的好奇,于是小明同学接着了解了一个和monorepo同样密切相关的工具------lerna

什么是lerna

Lerna是一个用来优化托管在上的多代码库的工作流的一个管理工具,可以让你在主项目下管理多个子项目,从而解决了多个包互相依赖,且发布时需要手动维护多个包的问题。像babel使用的就是lerna进行管理。

lerna的使用

  1. lerna使用之前需要全局安装lerna工具
json 复制代码
npm install lerna -g
  1. 初始化,即便已经有一个仓库了,仍然可以通过lerna init去添加lerna,如果使用workspace,lerna默认会用workspace里的模式,不需要添加其他参数。
json 复制代码
lerna init

(lerna初始化伴随着git init)

  1. 创建一个新的由lerna管理的包
json 复制代码
lerna create @c-demo/cli
  1. 安装依赖项
  • 在最外层公共node_modules增加lodash模块
json 复制代码
lerna add lodash
  • 在指定项目中增加axios模块
json 复制代码
lerna add axios --scope @c-demo/cli
  • 增加内部模块之间的依赖
json 复制代码
lerna add @c-demo/cli-utils --scope @c-demo/cli

注意,lerna在7.0.0版本中移除了lerna bootstap``lerna addlerna link的命令,因为比起lerna,具备workspace特性的包管理更适合做这些任务。

文档参考:

项目初衷 | pnpm

Legacy Package Management | Lerna

ps: 有关软硬链接我们下次再见~~~

相关推荐
Jiaberrr14 分钟前
页面转 PDF 功能的实现思路与使用方法
前端·javascript·vue.js·微信小程序·pdf·uniapp
XDU小迷弟33 分钟前
第2天:Web应用&架构类别&源码类别&镜像容器&建站模版&编译封装&前后端分离
服务器·前端·安全·web安全·架构·安全架构
热情仔1 小时前
win10 npm login 登陆失败
前端·npm·node.js
_.Switch1 小时前
FastAPI 响应模型与自定义响应
开发语言·前端·数据库·python·fastapi·命令模式
三天不学习1 小时前
Vue Router v3.x 路由进阶【路由篇】
前端·vue.js·路由·router·vue router
dowhileprogramming1 小时前
Python 中常见的数据结构之一嵌套字典
前端·数据结构·python
ryipei1 小时前
把vue项目或者vue组件发布成npm包或者打包成lib库文件本地使用
前端·vue.js·npm
赵大仁2 小时前
Uniapp中使用`wxml-to-canvas`开发DOM生成图片功能
前端·javascript·微信小程序·uni-app
雯0609~2 小时前
uni-app:实现普通选择器,时间选择器,日期选择器,多列选择器
前端·css·uni-app
一个处女座的程序猿O(∩_∩)O2 小时前
前端如何判断多个请求完毕
前端·javascript