pnpm梳理
创建于:2025-09-09 16:10:34字数:5490
pnpm 相比 npm/yarn 来说,最大的进步就是:可以复用多个项目中重复的依赖,节省磁盘空间。
如果使用npm/yarn安装依赖,当电脑中存在多个前端项目时,即使每一个项目都是安装的相同的依赖包,每个项目中也都会存在独立的一份依赖,这就很不科学,浪费磁盘空间。
在早期的npm中,打包的规则如下:
假设一个项目安装了依赖
"dependencies": {
"webpack": "^xx",
"babel": "^xx",
}
那么安装完就是这样
# 项目根目录
└── node_modules/ # 项目依赖根目录
├── webpack/ # 项目直接依赖:webpack
│ └── node_modules/ # webpack的嵌套依赖
│ ├── aaa/ # webpack依赖的aaa包
│ ├── bbb/ # webpack依赖的bbb包
│ └── anymatch/ # webpack依赖的anymatch包(第一次存储)
│
└── babel/ # 项目直接依赖:babel
└── node_modules/ # babel的嵌套依赖
├── ccc/ # babel依赖的ccc包
└── anymatch/ # babel依赖的anymatch包(第二次存储,重复)
注意,webpack 和 babel 都引用了一个 anymatch 的依赖,但是这个依赖被创建了2次,占了2份的磁盘空间,这显然是不科学的。
产生的问题:
- 公共的依赖会复制很多次,会占据比较大的磁盘空间。
- windows 的文件路径长度有限制,嵌套层级过深很容易导致系统错误。
为了解决这两个问题,后面的版本中 npm/yarn 就改成了平铺的模式。
那么安装完就是这样
# 项目根目录
└── node_modules/ # 项目依赖根目录
├── webpack/ # 项目直接依赖:webpack
├── babel/ # 项目直接依赖:babel
├── aaa/ # webpack的依赖(被平铺)
├── bbb/ # webpack的依赖(被平铺)
├── ccc/ # babel的依赖(被平铺)
└── anymatch/ # 共享依赖(只存储一次,但暴露为顶层依赖)
这平铺确实解决了多次创建依赖的问题。
新的问题:
-
项目中的dependencies没有:anymatch,aaa, bbb, ccc,但是项目中却可以直接 import 使用,这就是幽灵依赖问题。
-
即使 npm/yarn 使用平铺的模式能节省一部分空间,那也只局限于单个项目中。多个工程项目中,假设每一个工程都引入了axios这个包,那么每个工程都会有一份独立的axios的平铺依赖,所以平铺这种模式也只是解决了一个小问题而已。
为了解决新的问题,pnpm出现。
pnpm优势
- 可以复用多个项目中重复的依赖,节省磁盘空间
- 快速,比其他包管理器快2倍。
- 高效,node_modules 使用软硬链接进行寻址存储库。
- 支持 monorepos,pnpm内置支持单仓多包。
- 严格,pnpm创建的node_modules不是平铺的,而是有层次的嵌套依赖。防止幽灵依赖的问题。
pnpm 如何实现这些优点的
了解pnpm之前,必须前置理解一些概念。
软连接和硬链接
下图:左-软连接,右-硬链接
硬链接
当磁盘中存在一份Data,可以是视频文件、word文档等。这个Data硬链接可以创建多个,并且每一个硬链接都可以对这个Data进行读取和修改操作,并且修改后其他的硬链接的Data也同步更新变化,因为他们从根本上就是链接的同一个数据。
要注意和复制粘贴区分:
复制粘贴或者说是copy,是把磁盘数据复制一份写入了磁盘,此时磁盘是两份数据了,而硬链接只是一份数据。复制粘贴是真正的磁盘数据备份。
使用 ln 命令创建硬链接
当其中一个文件修改内容时,另一个文件也会实时同步更新内容。
以下使用mac做的演示:
使用 ln 1.js 1_hard.js
命令创建出一个 1_hard.js
硬链接文件,如下图:
当改变1_hard.js
文件的内容并保存时,1.js的内容也同步改变了,如下图:
软链接
软连接参考windows下的快捷方式
注意:软连接可以连接到文件夹!这一点和硬链接不同。
如果软连接到文件夹(window 使用命令 mklink /d xxx xxx),整个文件夹的内容都会被连接一份,像是复制了一份文件夹。
ln -s 做演示
在Mac和windows下:
- 当修改2_soft.js时,2.js也被实时修改了。
- 当修改2.js时,2_soft.js没有被实时修改,重新打开2_soft.js就可以看到被修改的内容了。
- 当删除2.js时,2_soft.js 为不可用状态。
了解完了软、硬链接,看看pnpm是如何使用它们创建依赖目录的
在mac系统上使用pnpm安装了一个mime-types进行演示。
-
当项目中引用 mime-types
时,会去根目录的 node_modules
中寻找 mime-types
包,这包的文件夹是个软连接,软链接到 .pnpm 中的 mime-types@3.0.1
中的 mime-types
。
这里使用软连接的好处就是防止多层嵌套的路径过长的问题,以及幽灵依赖的问题。
-
有一个专门存储npm包中各种小文件的目录,使用 pnpm store path
命令可以查看,/Users/baiwang/Library/pnpm/store/v10
。 这个目录mime-types@3.0.1
中的 mime-types
的每一个文件都硬链接到这个目录中对应的文件,这样的好处就是所有相同的文件只有一份磁盘占用,非常节省磁盘空间。
-
mime-types
引用的其他包,比如mime-db
,会在同级目录下创建一个软连接的 mime-db
,软链接到 .pnpm 中的 mime-types@1.54.0
中的 mime-db
。
-
mime-types@1.54.0
中的 mime-db
同样也会硬链接到 /Users/baiwang/Library/pnpm/store/v10
这个目录。
通过 “统一存储目录 + 硬链接复用文件 + 软链接构建项目依赖路径” 的组合,pnpm 同时解决了 npm/yarn 面临的磁盘浪费、幽灵依赖、路径过长等问题,实现了高效、省空间且规范的依赖管理。
pnpm 的缺点
凡事都有两面性,通过询问ai和结合自身踩坑经验,总结出几个我认为比较重要的缺点:
1. 兼容问题
-
在 Windows 系统或 WSL环境下,由于文件系统对链接的处理方式差异,有时可能会遇到权限问题或链接无法正常创建的情况。window对于pnpm确实不太友好,之前用过一阵win开发,使用pnpm是各种问题,最后就直接用npm了。
-
许多库会在postinstall(安装后)脚本中执行依赖检查、文件生成等操作,但部分脚本可能硬编码了 npm 的node_modules路径(如假设依赖在../node_modules下),而 pnpm 的符号链接结构会导致这些脚本执行失败。我遇到过next.js 的 standalone 模式使用 pnpm 安装有问题。应该和这个有关,具体什么问题记不清了。
2. 脚本不能随意更改
- 极少的时候,我们会去修改
node_modules
中的源码调试。但如果你修改了代码忘记还原了,问题就大了,因为整个电脑的前端项目的相同的第三方包都是引用同一批文件,其他的引用这个包的项目也会同步受到影响,一旦出现问题,很难找到问题的原因,所以千万不要给自己挖坑。
使用建议
-
新项目可以用pnpm,老的项目还是维持原样,不要随意迁移到pnpm,大概率会出现一些隐藏很深的bug。
-
开发环境使用pnpm,生产环境尽量还是使用npm,减少bug的出现,毕竟越精密的东西出问题的概率越高。
最后编辑于:2025-09-17 18:31:30
©著作权归作者所有,转载或内容合作请联系作者。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,little笔记系信息发布平台,仅提供信息存储服务。