pnpm 包管理工具

136 阅读6分钟

传统包管理工具的缺点

yarn和npm3 之后都是采用扁平化结构(比如说项目里安装了vue,vue又依赖了bable-loader、axios、lodash,这三个依赖都会放在node_modules下面),将依赖包提升到顶层,减少嵌套层级。虽然一定程度上节省了空间,但是依然会造成磁盘的浪费

如果磁盘上存在大量项目的话,每个项目下面都会下载很多重复的依赖,这是npm很大的一个缺点

pnpm的的出现解决了npm/yarn 的这个缺点

npm目录结构.png

pnpm

pnpm官网

pnpm是一个速度快,节省磁盘空间的包管理器,全称是 “Performant NPM”,即高性能的 npm。

它通过软链接和硬链接结合组织依赖的方式,大大节省了磁盘空间,提升了下载效率,同时解决了幻影依赖。

快速: pnpm 比 npm 快了近 2 倍

高效: node_modules 中的所有文件均克隆或硬链接自单一存储位置

支持单体仓库: pnpm 内置了对单个源码仓库中包含多个软件包的支持

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

硬链接

硬连接是电脑文件系统中的多个文件平等地共享同一个文件存储单元。

删除一个文件名后,还可以使用其他名字继续访问该文件。但是要修改一个文件里面的内容,访问其他文件的时候也会发生相同的变化,因为他们指向的是同一块存储单元

软连接

软连接是一类特殊的文件。其包含有一条以绝对路径或者相对路径的形式指向其他文件或者目录的引用

pnpm做了什么

pnpm是怎么存放文件的

如果使用了pnpm,依赖包将被存放在一个统一的位置

当使用pnpm add xxx的时候,pnpm会把所下载的文件保存在.pnpm的目录下,.pnpm中的文件最终是存放在磁盘的文件存储单元,项目中使用到依赖的时候会硬连接到磁盘的存储单元。这就是说为什么多个项目可以实现依赖共享。

pnpm目录结构.png

如果统一依赖包使用相同的版本,那么磁盘上只有这个依赖包的一份文件;

如果你对同一依赖包需要使用不同的版本,则仅有版本之间的不同文件会被存储起来;

所有文件都保存在硬盘上的统一位置:

    当安装软件包时,其包含的所有文件都会硬连接到此位置,而不会占用额外的硬盘空间;

    这让开发者可以在项目之间共享相同版本的依赖包;

pnpm创建非扁平的node_modules

例如:

使用命令 pnpm add vue,下载vue依赖

Progress: resolved 23, reused 23,downloaded 0, added 23

resolved 23:23个项目被重新解析。

reused 23: 23个项目被重用。

downloaded:0个下载   (说明所有依赖都来自缓存,没有新的下载)

added 23:新增了23个项目是从缓冲中提取或解析的结果。

vue3下载.png

vue.png

这是node_modules文件夹,根目录下只有vue一个文件,这个文件夹有一个箭头,类似于快捷方式,但这个跟快捷方式一样,这是一个软连接指向的.pnpm里面的 硬链接,指向的是vue@3.5.13

.pnpm目录

.pnpm.png

vue@3.5.13

image.png

vue里面会用到其他的依赖,vue的目录下用到的依赖是软连接,指向的是最外层 node_modules下的硬链接,pnpm通过这种软硬链接结合的方式 实现的非扁平结构,这种非扁平node_modules避免了 幻影依赖

幻影依赖

npm和yarn采用 扁平的node_modules,当只下载了一个vue, 所有依赖都在node_modules根目录下

npm_node_modules.png

npm_package.json.png

在package.json中只有vue一个依赖,但是node_modules中出现了很多依赖,这些没有在package.json中记录的依赖就是幻影依赖,这些依赖在项目中通过require 或者import 是可以直接引入并使用的。

幽灵依赖造成的问题

幽灵依赖至少造成了下面几个问题:

版本问题

幽灵依赖版本问题.png

如上图所示,在项目中下载了一个依赖A 版本是v1, 依赖A又依赖一个依赖B ,B的版本也是v1。

在此过程中并没有手动安装依赖B,但是在项目里边仍然可以去导入它并且使用,那么这个B就是幽灵依赖。

如果有一天将依赖A升级到了v2,依赖A所需的依赖B也要进行升级,B升级之后,它的一些API会发生变化或者摒弃,那么之前的代码就会出现问题。

依赖丢失

幽灵依赖丢失.png

除了版本问题之外还会出现依赖丢失的问题,如果依赖A发生了更新不再需要依赖B了,就是发生依赖丢失

还有一种情况,当某天不再需要依赖A了,依赖A被卸载 依赖B也会被卸载,项目中还用到了依赖B也会发生报错。

pnpm 解决了这些问题,在项目中只能使用package.json中记录过的依赖

pnpm常用命令

pnpm install

用于安装项目的所有依赖项。

pnpm update

基于指定的范围更新包到它们的最新版本。

pnpm_update.png

pnpm add

pnpm add pkgName

安装软件包及其依赖的任何软件包。 默认情况下,任何新软件包都被安装为生产依赖项。

pnpmAdd.png

pnpm run

运行在软件包清单文件中定义的脚本。

pnpm list

此命令将以树结构的形式输出已安装的所有软件包的版本以及及其依赖项。

pnpm store path

返回激活的存储目录的路径。

更多指令请查看官方文档

pnpm store

pnpm 在使用时会在磁盘下创建一个.pnpm-store文件夹,.pnpm-store 是pnpm的全局包存储目录,而项目中 node_modules 中的文件是通过硬链接(hard link)指向 .pnpm-store 中的实际文件。不同的项目之间共享这些包。这种方式是 pnpm 的核心设计之一,用于优化磁盘空间和安装速度。 在项目中可以通过pnpm store path 来获取当前项目的存储路径。

pnpmstorepath.png

storeList.png

根据返回的路径找到文件夹,进去发现文件名并非在node_modules中用到的那样,因为.pnpm-store中的文件名是通过算法计算后的哈希值。这样做是为了:

确保文件的唯一性和确定性

 相同内容的包只会存储一次,避免重复。 不同版本的包或内容不同的包会有不同的哈希值,从而避免冲突。