1.介绍
pnpm 是一个快速、节省磁盘空间的 JavaScript 包管理器,通过硬链接 共享依赖和软链接组织 node_modules 结构,比 npm 和 yarn 更高效。核心思想:是多个项目共用一个相同的依赖。
2.看看效果
我们通过创建一个vue项目看看依赖包是如何被创建和引用的。
sh
# 使用pnpm创建Vue项目
pnpm create vue@latest my-vue-project
# 进入项目目录
cd my-vue-project
# 安装依赖
pnpm install
输出的node_modules下的内容

展开的 node_modules/.pnpm

我们可以看到 node_modules/.pnpm/vue@3.5.22
我们主要关注node_modules 下的 .pnpm/vue@3.5.22
和 vue
- .pnpm/vue@3.5.22 (硬链接 连接到总仓库)
- vue 软链接 (链接到上面硬链接,解决导入时候的路径问题)
pnpm 依赖目录结构示意图
perl
# 全局存储区(store)
~/.pnpm-store/v3/
└── vue@3.5.22/ # 存放实际包内容(只保存一份)
├── package.json
├── index.js
└── ...
# 项目 A
projectA/
└── node_modules/
├── .pnpm/ # pnpm 的内部目录(存放硬链接)
│ └── vue@3.5.22/
│ └── node_modules/
│ └── vue/ # 硬链接 → ~/.pnpm-store/v3/vue@3.5.22/*
│ ├── package.json
│ ├── index.js
│ └── ...
│
└── vue ---> .pnpm/vue@3.5.22/node_modules/vue
(符号链接 symlink)
# 项目 B
projectB/
└── node_modules/
├── .pnpm/ # 这里同样是硬链接
│ └── vue@3.5.22/
│ └── node_modules/
│ └── vue/ # 硬链接 → ~/.pnpm-store/v3/vue@3.5.22/*
│
└── vue ---> .pnpm/vue@3.5.22/node_modules/vue
1. pnpm 的依赖存储机制
pnpm 在本地会维护一个全局的包存储目录(store),默认路径大概是:
javascript
~/.pnpm-store/v3
- store 里保存了所有版本的包内容 (比如
vue
、vite
),是按内容寻址的(基于内容哈希),避免重复下载和存储。 - 当不同项目依赖同一个版本的包时,pnpm 不会再复制,而是通过 硬链接 来复用。
2. 硬链接 (hard link)
硬链接是 文件系统级别 的"多入口"。同一个物理文件可以有多个路径引用:
- 当你在项目里运行
pnpm install
,pnpm 会把包的实际文件 硬链接 到项目的node_modules/.pnpm
目录。 - 这样每个项目都像有一份完整的包副本,但实际上磁盘上只有一份数据(inode 相同,引用计数增加)。
- 优点:不同项目、不同
node_modules
共享相同的包内容,不会浪费磁盘空间。
例子(假设 lodash
已经存在于 store):
bash
projectA/node_modules/.pnpm/vue@3.5.22/node_modules/vue/ # 硬链接
projectB/node_modules/.pnpm/vue@3.5.22/node_modules/vue/ # 硬链接
这两个路径下的文件,实际上都指向 ~/.pnpm-store/v3/.../vue@3.5.22
里的真实内容。
3. 符号链接 (symlink)
硬链接解决了内容复用问题,但 Node.js 的 require()
/import
是依赖于 目录结构解析 的,必须能在 node_modules
里按照规范找到依赖。
所以 pnpm 还需要用 符号链接 来构建可解析的依赖树。
比如一个包 foo
依赖 bar
:
bash
project/node_modules/foo # symlink → node_modules/.pnpm/foo@1.0.0/node_modules/foo
project/node_modules/bar # symlink → node_modules/.pnpm/bar@2.0.0/node_modules/bar
最终形成的目录结构:
node_modules/.pnpm/...
目录下:存放硬链接的真实文件。node_modules/
根目录:存放符号链接,指向.pnpm
里的硬链接目录。
这样:
- 硬链接保证了磁盘利用率和速度。
- 符号链接保证了
require('foo')
能正常解析。
4. 优势总结
- 节省磁盘空间:相同版本的依赖只存储一次。
- 快速安装:安装时只是做链接,不需要复制大文件。
- 隔离性 :不同项目依然有自己的
node_modules
目录,不会像npm link
那样污染全局。
3.linux中的 硬链接 和 软链接
核心概念
索引节点(inode)
在理解链接之前,需要先了解 inode:
- 每个文件都有一个唯一的 inode 编号
- inode 存储文件的元数据(权限、所有者、大小、时间戳等)
- inode 指向文件数据在磁盘上的实际存储位置
bash
# 查看文件的 inode 号
ls -i filename.txt
# 输出: 123456 filename.txt
硬链接(Hard Link)
硬链接是多个文件名指向同一个 inode 的机制。所有硬链接都是平等的,没有原始和副本之分。
创建硬链接
bash
# 创建硬链接
ln original.txt hardlink.txt
# 查看链接情况
ls -li
# 输出示例:
# 123456 -rw-r--r-- 2 user group 1024 Jan 1 12:00 original.txt
# 123456 -rw-r--r-- 2 user group 1024 Jan 1 12:00 hardlink.txt
# ↑ 相同的inode ↑链接计数
硬链接的特点
1. 相同的 inode 编号
bash
# 查看 inode
ls -i original.txt hardlink.txt
# 输出: 123456 original.txt 123456 hardlink.txt
2. 共享链接计数
bash
# 查看链接计数
ls -l
# 输出: -rw-r--r-- 2 user group 1024 Jan 1 12:00 original.txt
# -rw-r--r-- 2 user group 1024 Jan 1 12:00 hardlink.txt
# 这里的 "2" 表示有2个硬链接指向这个inode
3. 删除行为
bash
# 删除一个硬链接不会影响其他链接
rm original.txt
ls -l hardlink.txt
# hardlink.txt 仍然存在且内容完整
# 只有当链接计数为0时,文件数据才会被真正删除
4. 限制
bash
# 不能创建目录的硬链接
ln dir1 dir2_hardlink # 错误: 不允许的操作
# 不能跨文件系统创建硬链接
ln /mnt/disk1/file.txt /mnt/disk2/hardlink.txt # 错误: 无效的跨设备链接
软链接(Soft Link / Symbolic Link)
什么是软链接
软链接是一个特殊的文件,包含指向另一个文件或目录的路径。类似于 Windows 的快捷方式。
创建软链接
bash
# 创建软链接
ln -s original.txt softlink.txt
# 查看链接情况
ls -li
# 输出示例:
# 123456 -rw-r--r-- 1 user group 1024 Jan 1 12:00 original.txt
# 789012 lrwxrwxrwx 1 user group 12 Jan 1 12:01 softlink.txt -> original.txt
# ↑ 不同的inode ↑链接类型为'l'
软链接的特点
1. 不同的 inode 编号
bash
# 查看 inode
ls -i original.txt softlink.txt
# 输出: 123456 original.txt 789012 softlink.txt
2. 文件类型和权限
bash
ls -l softlink.txt
# 输出: lrwxrwxrwx 1 user group 12 Jan 1 12:01 softlink.txt -> original.txt
# ↑ 'l' 表示链接文件,权限通常是 777(但实际权限由目标文件决定)
3. 删除行为
bash
# 删除原始文件后,软链接成为"悬空链接"
rm original.txt
ls -l softlink.txt
# 输出: lrwxrwxrwx 1 user group 12 Jan 1 12:01 softlink.txt -> original.txt
# 但访问它会报错: No such file or directory
# 删除软链接不影响原始文件
rm softlink.txt
ls -l original.txt # 原始文件仍然存在
4. 灵活性
bash
# 可以链接到目录
ln -s /path/to/dir symlink_to_dir
# 可以跨文件系统链接
ln -s /mnt/disk1/file.txt /mnt/disk2/symlink.txt # 允许
# 可以创建相对路径链接
ln -s ../other-dir/file.txt relative_link.txt