众生皆苦,我选pnpm(一)

简介: 众生皆苦,我选pnpm

1686892584895.jpg

概述

pnpm - 速度快、节省磁盘空间的软件包管理器

perfomance npm,即pnpm(高性能npm)

优势

  • 快速
  • pnpm 是同类工具速度的将近 2 倍
  • 高效
  • node_modules 中的所有文件均链接自单一存储位置
  • 支持monorepos
  • pnpm 内置了对单个源码仓库中包含多个软件包的支持

注:这个东西这么读monorepos = Monolithic repository /ˌmänəˈliTHik/ /rəˈpäzəˌtôrē/

  • 严格
  • pnpm 创建的 node_modules 默认并非扁平结构,因此代码无法对任意软件包进行访问

以上是4条优势是官网的说明和宣传,后面我们会针对npm的发展历史中存在的问题

来对比说明,pnpm的提出动机,pnpm 的优势在哪里,为什么具备这些优势。

npm

npm 全称,Node Package Manager node包管理工具

执行npm install 之后。npm 帮我们下载对应的依赖包并解压到本地缓存,然后构造node_modules目录结构,写入依赖文件,对应的node_modules内部结构也经历了几个版本的变化。

npm v1/v2 嵌套依赖

最开始其实没有注重npm包的管理,只是简单的嵌套依赖,这种方式层级依赖结构清晰

但是随着npm包的增多,项目的迭代扩展,重复包越下载越多,造成了空间浪费,导致前端本地项目node_modules 动辄上百M

在业务开发中,安装几个项目,项目体积好几G,对使用者们极其不友好。

入下图所示,依赖包C 在AB 中都被引用了, 被重复下载了两次,其实是两个完全相同的东西。

从我们现在的角度看,完全没有必要。

npm v3 扁平化

node_modules体积过大,嵌套过深

npm 团队也意识到这个问题,通过扁平化的方式,将子依赖安装到了主依赖所在项目中,以减少依赖嵌套太深,和重复下载安装的问题。

如下图所示,A 的依赖项C 被提升到了顶层,如果后续有安装包,也依赖C,会去上一级的node_modules查找,如果有相同版本的包,则不会再去重复下载,直接从上一层拿到需要的依赖包C

说明:为什么自己的node_modules没有C,也能在上层访问到C呢?

require 寻找第三方包,会每层级依次去寻找node_modules,所以即便本层级没有node_moudles,上层有,也能找到

1686892640841.jpg

扁平化方式解决了相同包重复安装的问题,也一定程度上解决了依赖层级太深的问题。

为什么说是一定程度上?

因为如上图所示,B 依赖的C v2.0.0,并没有提升,依然是嵌套依赖。

因为在两个依赖包 C 的版本号不一致,只能保证一个在顶层,上图所示C v1.0.0 被提升了,v2.0.0 没有被提升,后续v2.0.0 还是会被重复下载,所以当出现多重依赖时,依然会出现重复安装的问题。\

而且这个提升的顺序,也不是根据使用量优先提升,而是根据先来先服务原则,先安装的先提升。这会导致不确定性问题,随着项目迭代,npm i 之后得到的node_modules目录结构,有可能不一样。

与此同时,我们把C,提升到了顶层,即使项目package.json,没有声明过C,但是也可以在项目中引用到C,这就是幽灵依赖问题。


可以说 npm v3 在解决嵌套依赖,重复安装问题的同时,又带来了新的问题。

npm v5 lock

npm v5 借鉴yarn的思想,新增了package-lock.json

该文件里面记录了package.json依赖的模块,以及模块的子依赖。并且给每个依赖标明了版本、获取地址和验证模块完整性哈希值。

通过package-lock.json,保障了依赖包安装的确定性与兼容性,使得每次安装都会出现相同的结果。

这个就解决了不确定性的问题

package-lock.json文件字段说明

1686892663601.jpg

  • name:项目的名称;
  • version:项目的版本;
  • lockfileVersion:lock文件的版本;
  • requires:使用requires来跟踪模块的依赖关系;
  • dependencies:项目的依赖
  • version表示实际安装的版本;
  • resolved用来记录下载的地址,registry仓库中的位置;
  • requires记录当前模块的依赖;
  • integrity用来从缓存中获取索引,再通过索引去获取压缩包文件

npm install 过程

至此我们也可以顺带总结一下npm install的全过程

npm install先检测是有package-lock.json文件:

  • 没有package-lock.json文件
  • 分析依赖关系,这是因为我们可能包会依赖其他的包,并且多个包之间会产生相同依赖的情况;
  • 从registry仓库中下载压缩包(如果我们设置了镜像,那么会从镜像服务器下载压缩包);
  • 获取到压缩包后会对压缩包进行缓存(从npm5开始有的, npm config get cache 可以查看地址)
  • 将压缩包解压到项目的node_modules文件夹中
  • 有package-lock.json文件
  • 检测lock中包的版本是否和package.json中一致
  • 不一致,那么会重新构建依赖关系,直接会走上面的流程;
  • 一致的情况下,会去优先查找缓存
  • 缓存没有找到,从registry仓库下载,直接走上面流程;
  • 命中缓存会获取缓存中的压缩文件
  • 将压缩文件解压到node_modules文件夹中;

1686892684145.jpg

pnpm

综上,基于npm扁平化node_modules的结构下,虽然解决了依赖嵌套、重复安装的问题,但多重依赖和幽灵依赖并没有好的解决方式。

pnpm出现就是为了解决现在npm 存在的问题,正如官网pnpm 所形容自己的是一款速度快,节省磁盘空间的软件包管理器。

1686892714051.jpg

前置知识 软链接&硬链接

简单理解

硬链接就是多个文件名指向了同一个文件,这多个文件互为硬链接。

像是JS 中的两个相同的对象,a 和b 的真实内容指向堆中同一个地址,修改一个,同时改变,一荣俱荣,一损俱损。删除一个,并不影响另一个。

let a = {test:1} 
let b = a
a.test = 2
console.log(b) // {test:2}

软链接就是快捷方式,是一个单独文件。

就像我们电脑桌面上的快捷方式,大小只有几字节,指向源文件,点击快捷方式,其实执行的就是源文件。

专业理解

在 Linux 的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。A 是 B 的硬链接(A 和 B 都是文件名)则 A 文件中的 inode 节点号与 B 文件的 inode 节点号相同,即一个 inode 节点对应两个不同的文件名,两个文件名指向同一个文件,

软硬链接 是linux 中解决文件的共享使用问题的两个方式,目的也是为了节省磁盘空间。

大家可以去网上找找专业教程,或者报名山月的linux 训练营,这里就不展开说了。

node_modules的层级结构

比如某项目中,package.json里声明了A和B,

A的package.json 里声明了C v1.0.0,B的package.json 里声明了C v2.0.0


1686892754097.jpg

进行pnpm i 之后,node_modules的层级结构如下

双键头代表硬链接

单箭头代表软链接

node_modules
|_ A -> .pnpm/A@1.0.0/node_modules/A
|_ B -> .pnpm/B@1.0.0/node_modules/B
|_ .pnpm
  |_ A@1.0.0
    |_ node_modules
      |_ A => pnpm/store/A 
      |_ C -> ../../C@1.0.0/node_modules/C
  |_ B@1.0.0
    |_ node_modules
      |_ B => pnpm/store/B 
      |_ C -> ../../C@2.0.0/node_modules/C
  |_ C@1.0.0
    |_ node_modules
      |_ C => pnpm/store/C 
  |_ C@2.0.0
    |_ node_modules
      |_ C => pnpm/store/C

以A 包为例,A的目录下并没有node_modules,是一个软链接,真正的文件位于 .pnpm/A@1.0.0/node_modules/A 并硬链接到全局store中。

A 和 B 是我们在项目package.json中声明的依赖包,node_modules除了A,B 没有其他包,说明不是扁平化结构。也就不存在 幽灵依赖的问题

.pnpm 中存放着所有的包。最终硬链接指向指向全局pnpm 仓库里的store目录下。

也就是说,我们所有的包,最终都以硬链接的形式,最终都在全局 pnpm/store 中,可以使得不同的项目从全局 store 寻找到同一个依赖,大大节省了磁盘空间

如果上面这个文件列表不够直观,大家也可以看我参考官网画的结构图


1686892808552.jpg


相关文章
|
5月前
|
移动开发 安全 前端开发
分享97个社区论坛PHP源码,总有一款适合你
分享97个社区论坛PHP源码,总有一款适合你
1333 0
|
4月前
|
应用服务中间件 Linux 程序员
老程序员分享:nginx安装及其配置详细教程
老程序员分享:nginx安装及其配置详细教程
|
12月前
|
Web App开发 IDE Java
手把手教你下载安装Goland 新手别错过!
手把手教你下载安装Goland 新手别错过!
356 0
|
机器学习/深度学习 数据采集 人工智能
我也曾经因安装库而抓狂,直到我遇到了
我自己印象最深的是在 Mac 上安装 pycrypto(加密)和 scipy(科学计算)模块,折腾了很久。因为这类模块并不是单纯的 Python 代码,而是需要调用诸如 C 语言的库,于是就牵涉到在不同平台上的编译,有些还不能很方便地用虚拟环境分离版本。
|
存储 数据安全/隐私保护 Windows
众生皆苦,我选pnpm(二)
众生皆苦,我选pnpm
125 0
|
存储 资源调度 JavaScript
众生皆苦,我选pnpm(三)
众生皆苦,我选pnpm
298 0
|
存储 缓存 固态存储
昨晚,我体会了没有 pnpm 的痛(一)
昨晚,我体会了没有 pnpm 的痛(一)
115 0
|
存储 资源调度 Linux
昨晚,我体会了没有 pnpm 的痛(二)
昨晚,我体会了没有 pnpm 的痛(二)
110 0
|
JavaScript
来自阿里的,7天学会(废)nodejs教程
当然这也是我们成为全栈必须要掌握的知识。 今天推荐一个适合新手的免费node学习教程 - 《7天学会nodejs》。
178 0
来自阿里的,7天学会(废)nodejs教程
|
自然语言处理 IDE Java
有了这12款idea插件后,室友再也不叫我小白了
搞Java开发用什么软件,当然是神器idea了,那么,idea的插件对于你来说就是必不可少的了,不仅可以提高自己的编码效率,还可以减轻工作时的枯燥烦闷。接下来就来说说,作为一名小白,我在平时敲代码用的什么插件吧。
264 0
有了这12款idea插件后,室友再也不叫我小白了