跳到主要内容
版本:Next

常见问题

如果包存储在全局存储中,为什么我的 node_modules 文件夹还会占用磁盘空间?

pnpm 从全局存储创建硬链接到项目的 node_modules 文件夹。硬链接指向磁盘上原始文件所在的相同位置。例如,如果你的项目中有一个依赖 foo 占用了 1MB 空间,那么它在项目的 node_modules 文件夹中看起来会占用 1MB 空间,在全局存储中也占用相同的空间。但是,这 1MB 实际上是磁盘上的同一块空间,只是从两个不同的位置进行访问。所以 foo 总共只占用 1MB,而不是 2MB。

更多相关内容:

它能在 Windows 上运行吗?

简短回答:可以。 详细回答:在 Windows 上使用符号链接确实比较麻烦,但是 pnpm 有解决方案。对于 Windows,我们使用联接点来代替。

但是嵌套的 node_modules 方式不是与 Windows 不兼容吗?

npm 的早期版本因为嵌套所有 node_modules 而出现问题(参见[这个问题])。然而,pnpm 不会创建深层文件夹,它以扁平方式存储所有包,并使用符号链接创建依赖树结构。

循环符号链接会怎样?

虽然 pnpm 使用链接将依赖项放入 node_modules 文件夹,但由于父包与其依赖项被放置在同一个 node_modules 文件夹中,所以避免了循环符号链接。因此 foo 的依赖项不在 foo/node_modules 中,而是与 foo 一起存在于 node_modules 中。

为什么要使用硬链接?为什么不直接创建符号链接到全局存储?

同一个包在一台机器上可以有不同的依赖集。

在项目 A 中,foo@1.0.0 的依赖可能解析为 bar@1.0.0,但在项目 B 中,同样的 foo 依赖可能解析为 bar@1.1.0;因此,pnpm 会在每个使用它的项目中为 foo@1.0.0 创建硬链接,以便为其创建不同的依赖集。

直接创建符号链接到全局存储虽然可以通过 Node.js 的 --preserve-symlinks 标志实现,但这种方法会带来很多其他问题,所以我们决定继续使用硬链接。关于为什么做出这个决定的更多详细信息,请参见这个问题

pnpm 能在同一个 Btrfs 分区的不同子卷之间工作吗?

虽然 Btrfs 不允许在单个分区的不同子卷之间创建跨设备硬链接,但它允许使用 reflinks。因此,pnpm 利用 reflinks 在这些子卷之间共享数据。

pnpm 能跨多个驱动器或文件系统工作吗?

包存储应该与安装位置在同一个驱动器和文件系统上,否则包将被复制而不是链接。这是由于硬链接工作方式的限制,一个文件系统上的文件无法寻址另一个文件系统上的位置。更多详细信息请参见 Issue #712

pnpm functions differently in the 2 cases below:

指定存储路径的情况

如果通过存储配置指定了存储路径,那么在存储和位于不同磁盘上的任何项目之间会进行复制。

如果你在磁盘 A 上运行 pnpm install,那么 pnpm 存储必须在磁盘 A 上。 如果 pnpm 存储位于磁盘 B 上,那么所有需要的包都会直接复制到项目位置,而不是创建链接。这会严重影响 pnpm 的存储和性能优势。

未指定存储路径的情况

如果未设置存储路径,那么会创建多个存储(每个驱动器或文件系统一个)。

如果在磁盘 A 上运行安装,存储将在 A 的文件系统根目录下创建为 .pnpm-store。如果稍后在磁盘 B 上运行安装,将在 B 上的 .pnpm-store 创建一个独立的存储。项目仍然保持 pnpm 的优势,但每个驱动器可能会有冗余的包。

pnpm 代表什么?

pnpm 代表 performant npm(高性能的 npm)。 这个名字是由 @rstacruz 提出的。

pnpm 不能与 <YOUR-PROJECT-HERE> 一起使用?

在大多数情况下,这意味着其中一个依赖项需要未在 package.json 中声明的包。这是由扁平的 node_modules 导致的一个常见错误。如果发生这种情况,这是依赖项中的错误,应该修复该依赖项。 不过这可能需要一些时间,所以 pnpm 提供了一些解决方案来让有问题的包正常工作。

解决方案 1

如果遇到问题,你可以使用 nodeLinker: hoisted 设置。 这会创建一个类似于 npm 创建的扁平 node_modules 结构。

解决方案 2

在以下示例中,一个依赖项在其自己的依赖列表中没有 iterall 模块。

解决有问题包的缺失依赖项的最简单方法是 iterall 作为依赖项添加到我们项目的 package.json

你可以通过运行 pnpm add iterall 来实现,它会自动添加到你的项目的 package.json 中。

  "dependencies": {
...
"iterall": "^1.2.2",
...
}

解决方案 3

其中一个解决方案是使用钩子将缺失的依赖项添加到包的 package.json 中。

一个例子是 Webpack Dashboard,它以前不能与 pnpm 一起使用。现在这个问题已经解决,可以与 pnpm 正常工作了。

它曾经会抛出一个错误:

Error: Cannot find module 'babel-traverse'
at /node_modules/inspectpack@2.2.3/node_modules/inspectpack/lib/actions/parse

问题在于 babel-traversewebpack-dashboard 使用的 inspectpack 使用,但 babel-traverse 没有在 inspectpackpackage.json 中指定。它仍然可以与 npmyarn 一起工作,因为它们创建扁平的 node_modules

解决方案是创建一个包含以下内容的 .pnpmfile.cjs

module.exports = {
hooks: {
readPackage: (pkg) => {
if (pkg.name === "inspectpack") {
pkg.dependencies['babel-traverse'] = '^6.26.0';
}
return pkg;
}
}
};

创建 .pnpmfile.cjs 后,只需删除 pnpm-lock.yaml - 不需要删除 node_modules,因为 pnpm 钩子只影响模块解析。 然后,重新构建依赖项,它应该就能正常工作了。