全局虚拟存储

By default, pnpm creates a .pnpm directory inside each project's node_modules. This is the "virtual store". 它包含指向 内容寻址存储 中文件的硬链接。 Every project gets its own projection of this virtual store: pnpm hardlinks files from the content-addressable store into the .pnpm directory structure. 实际的文件内容在磁盘上只存在一次,但目录结构会为每个项目重新创建,以便 Node.js 的模块解析算法能够找到每个包的正确依赖项。

The global virtual store (virtualStoreType: global, spelled enableGlobalVirtualStore: true before v11.23.0) changes this. pnpm 不为每个项目都设置自己的 node_modules/.pnpm 目录,而是维护一个共享的虚拟存储(位于 <store-path>/links/,运行 pnpm store path 可以找到 <store-path>)。每个项目的 node_modules 目录下只包含指向这个共享位置的符号链接。

默认行为与全局虚拟存储#

默认(每个项目一个虚拟存储)#

project-a/
└── node_modules/
    ├── lodash → .pnpm/lodash@4.17.21/node_modules/lodash
    └── .pnpm/
        └── lodash@4.17.21/
            └── node_modules/
                └── lodash/ ← 指向内容寻址存储的硬链接
project-b/
└── node_modules/
    ├── lodash → .pnpm/lodash@4.17.21/node_modules/lodash
    └── .pnpm/
        └── lodash@4.17.21/
            └── node_modules/
                └── lodash/ ← 相同的硬链接,重复的目录结构

每个项目都有自己的 .pnpm 文件,其中包含硬链接。文件内容不会在磁盘上重复(硬链接共享 inode),但目录结构会重复。对于大型单体仓库或许多并行检出,在 pnpm install 期间创建数千个硬链接所花费的时间会累积。

使用全局虚拟存储#

project-a/
└── node_modules/
    └── lodash → <global-store>/links/@/lodash/4.17.21/<hash>/node_modules/lodash
project-b/
└── node_modules/
    └── lodash → <global-store>/links/@/lodash/4.17.21/<hash>/node_modules/lodash ← 相同目标

这两个项目都直接符号链接到全局虚拟存储中的同一位置。没有每个项目单独的 .pnpm 目录。 The global virtual store itself contains the hardlinks to the content-addressable store, but that happens only once per dependency graph (more on that below), not per project.

软件包标识如何工作#

在全局虚拟存储中,每个软件包目录都以其依赖关系图的哈希值命名。两个使用 lodash@4.17.21 且具有相同传递依赖树的项目将指向完全相同的目录。如果依赖关系树不同(例如,不同的对等依赖关系),pnpm 会创建单独的条目。这在概念上类似于 NixOS 管理软件包 使用依赖关系图哈希的方式。

何时使用它#

The global virtual store is most useful when you have multiple checkouts of the same project on disk. For example, when using git worktrees for multi-agent development. 在这种情况下,每个工作树都可以获得几乎无成本的 node_modules,因为所有真正的包内容都已存在于共享存储中。

它还可以加快同一台机器上不相关项目的安装速度,因为任何项目已经安装的任何软件包版本都可以立即使用。

局限性#

  • CI 环境:在 CI 中,通常没有缓存,因此没有可用的全局热存储。全局虚拟存储在 CI 中通常没用。
  • 共享信任域:全局虚拟存储和内容寻址存储属于共享可写状态。仅将其用于相互信任的项目、用户和作业,并利用文件系统权限保护存储路径。
  • ESM hoisting: pnpm uses the NODE_PATH environment variable to support hoisted dependencies with the global virtual store, and Node.js does not respect NODE_PATH for ESM imports. Since v11.23.0, every process pnpm spawns for the project (pnpm run, pnpm exec, lifecycle scripts, and the tools pnpm dlx runs) also gets a NODE_OPTIONS --import flag registering a resolve hook that restores those lookups, so a dependency importing a package it does not declare resolves under ESM too (#9618). A node process started outside pnpm does not get that environment, and setting extendNodePath to false turns the whole NODE_PATH mechanism off, resolve hook included; declare the missing dependencies with packageExtensions if you need them to resolve in either case.

注意

全局虚拟存储目前对于项目安装默认处于禁用状态,并被标记为实验性功能,因为某些工具可能无法正确处理符号链接的 node_modules。 You need to explicitly set virtualStoreType: global in pnpm-workspace.yaml to use it for project installs. 在 pnpm v11 中,默认情况下,通过 pnpm dlx (pnpx) 安装的软件包和全局安装的软件包都会启用全局虚拟存储。目标是在未来的版本中默认启用该功能。

全局软件包#

在 pnpm v11 中,全局安装(pnpm add -g)和 pnpm dlx 默认使用全局虚拟存储。有关 v11 中全局软件包管理如何工作的完整指南,包括隔离安装和新的二进制文件位置,请参阅 全局软件包。

配置#

See the virtualStoreType setting reference for all configuration details. virtualStoreType was added in v11.23.0 as the canonical spelling of enableGlobalVirtualStore, which keeps working.