常见问题
如果包存储在全局存储中,为什么我的 node_modules 文件夹仍然会占用磁盘空间?
🌐 Why does my node_modules folder use disk space if packages are stored in a global store?
pnpm 会从全局存储创建硬链接到项目的 node_modules 文件夹。硬链接指向磁盘上原始文件的相同位置。例如,如果你的项目中有 foo 作为依赖,并且它占用 1MB 的空间,那么它在项目的 node_modules 文件夹中看起来也占用 1MB 的空间,全局存储中也是相同的空间。然而,这 1MB 实际上是同一块空间,从两个不同位置访问。因此,foo 总共占用的空间是 1MB,而不是 2MB。
🌐 pnpm creates hard links from the global store to the project's node_modules
folders. Hard links point to the same place on the disk where the original
files are. So, for example, if you have foo in your project as a dependency
and it occupies 1MB of space, then it will look like it occupies 1MB of space in
the project's node_modules folder and the same amount of space in the global
store. However, that 1MB is the same space on the disk addressed from two
different locations. So in total foo occupies 1MB, not 2MB.
有关此主题的更多信息:
🌐 For more on this subject:
它可以在 Windows 上运行吗?
🌐 Does it work on Windows?
简短回答:是的。 详细回答:在 Windows 上使用符号链接至少可以说是有问题的,然而,pnpm 有一个解决方法。对于 Windows,我们使用[交汇点]代替。
🌐 Short answer: Yes. Long answer: Using symbolic linking on Windows is problematic to say the least, however, pnpm has a workaround. For Windows, we use junctions instead.
但是嵌套的 node_modules 方法与 Windows 不兼容吗?
🌐 But the nested node_modules approach is incompatible with Windows?
早期版本的 npm 存在问题,因为它会嵌套所有 node_modules(见 [此问题])。然而,pnpm 不会创建深层文件夹,它会将所有包平铺存储,并使用符号链接来创建依赖树结构。
🌐 Early versions of npm had issues because of nesting all node_modules (see
this issue). However, pnpm does not create deep folders, it stores all packages
flatly and uses symbolic links to create the dependency tree structure.
圆形符号链接怎么样?
🌐 What about circular symlinks?
尽管 pnpm 使用链接将依赖放入 node_modules 文件夹,但循环符号链接被避免,因为父包会被放置在其依赖所在的同一个 node_modules 文件夹中。因此,foo 的依赖不在 foo/node_modules 中,而 foo 会和它自己的依赖一起放在 node_modules 中。
🌐 Although pnpm uses linking to put dependencies into node_modules folders,
circular symlinks are avoided because parent packages are placed into the same
node_modules folder in which their dependencies are. So foo's dependencies
are not in foo/node_modules, but foo is in node_modules together with its
own dependencies.
为什么要有硬链接?为什么不直接指向全局存储的符号链接?
🌐 Why have hard links at all? Why not symlink directly to the global store?
一个包在一台机器上可以有不同的依赖集。
🌐 One package can have different sets of dependencies on one machine.
在项目 A 中,foo@1.0.0 的依赖可以解析为 bar@1.0.0,但在项目 B 中,foo 的同一依赖可能会解析为 bar@1.1.0;因此,pnpm 会将 foo@1.0.0 硬链接到每个使用它的项目,以便为它创建不同的依赖集合。
🌐 In project A foo@1.0.0 can have a dependency resolved to bar@1.0.0, but
in project B the same dependency of foo might resolve to bar@1.1.0; so,
pnpm hard links foo@1.0.0 to every project where it is used, in order to
create different sets of dependencies for it.
直接符号链接到全局存储在 Node 的 --preserve-symlinks 标志下是可行的,然而,这种方法会带来大量自身的问题,所以我们决定坚持使用硬链接。有关为何做出此决定的更多详细信息,请参见 这个问题。
🌐 Direct symlinking to the global store would work with Node's
--preserve-symlinks flag, however, that approach comes with a plethora of its
own issues, so we decided to stick with hard links. For more details about why
this decision was made, see this issue.
pnpm 是否可以跨一个 Btrfs 分区中的不同子卷工作?
🌐 Does pnpm work across different subvolumes in one Btrfs partition?
虽然 Btrfs 不允许在单个分区的不同子卷之间创建跨设备硬链接,但它允许使用 reflinks。因此,pnpm 利用 reflinks 在这些子卷之间共享数据。
🌐 While Btrfs does not allow cross-device hardlinks between different subvolumes in a single partition, it does permit reflinks. As a result, pnpm utilizes reflinks to share data between these subvolumes.
pnpm 可以跨多个驱动器或文件系统工作吗?
🌐 Does pnpm work across multiple drives or filesystems?
软件包存储应该与安装路径在同一个驱动器和文件系统上,否则软件包将被复制而不是链接。这是由于硬链接的工作方式存在限制,即一个文件在一个文件系统上无法指向另一个文件系统中的位置。更多详情请参见 Issue #712。
🌐 The package store should be on the same drive and filesystem as installations, otherwise packages will be copied, not linked. This is due to a limitation in how hard linking works, in that a file on one filesystem cannot address a location in another. See Issue #712 for more details.
pnpm 在以下两种情况下的功能有所不同:
🌐 pnpm functions differently in the 2 cases below:
存储路径已指定
🌐 Store path is specified
如果通过存储配置指定了存储路径,那么复制会在存储和任何位于不同磁盘上的项目之间进行。
🌐 If the store path is specified via the store config, then copying occurs between the store and any projects that are on a different disk.
如果你在磁盘 A 上运行 pnpm install,那么 pnpm 仓库存储必须在磁盘 A 上。
如果 pnpm 仓库存储位于磁盘 B,那么所有所需的包将会直接复制到项目位置,而不是通过链接方式。这会严重影响 pnpm 的存储和性能优势。
🌐 If you run pnpm install on disk A, then the pnpm store must be on disk A.
If the pnpm store is located on disk B, then all required packages will be
directly copied to the project location instead of being linked. This severely
inhibits the storage and performance benefits of pnpm.
存储路径未指定
🌐 Store path is NOT specified
如果未设置存储路径,则会创建多个存储(每个驱动器或文件系统一个)。
🌐 If the store path is not set, then multiple stores are created (one per drive or filesystem).
如果安装在磁盘“A”上,存储会在“A”上创建 文件系统根目录下的“.pnpm-store”。 如果安装在 磁盘“B”,将在“.pnpm-store”的“B”上创建独立存储。该 项目仍能保留PNPM的优势,但每个驱动项目可能都带来了 冗余封装。
🌐 If installation is run on disk A, the store will be created on A
.pnpm-store under the filesystem root. If later the installation is run on
disk B, an independent store will be created on B at .pnpm-store. The
projects would still maintain the benefits of pnpm, but each drive may have
redundant packages.
pnpm代表什么?
🌐 What does pnpm stand for?
pnpm 代表 performant npm。
@rstacruz 想出了这个名字。
pnpm 不适用于 <YOUR-PROJECT-HEREE> 吗?
🌐 pnpm does not work with <YOUR-PROJECT-HERE>?
在大多数情况下,这意味着某个依赖需要的包没有在 package.json 中声明。这是由于扁平化的 node_modules 引起的常见错误。如果发生这种情况,这是依赖中的错误,依赖本身应该被修复。不过,这可能需要一些时间,因此 pnpm 提供了变通方法来使有问题的包能够工作。
🌐 In most cases it means that one of the dependencies require packages not
declared in package.json. It is a common mistake caused by flat
node_modules. If this happens, this is an error in the dependency and the
dependency should be fixed. That might take time though, so pnpm supports
workarounds to make the buggy packages work.
解决方案 1
🌐 Solution 1
如果出现问题,你可以使用 nodeLinker: hoisted 设置。 这会创建一个平坦的 node_modules 结构,类似于 npm 创建的结构。
🌐 In case there are issues, you can use the nodeLinker: hoisted setting.
This creates a flat node_modules structure similar to the one created by npm.
解决方案 2
🌐 Solution 2
在下面的示例中,一个依赖没有在其自己的依赖列表中包含 iterall 模块。
🌐 In the following example, a dependency does not have the iterall module in
its own list of deps.
解决有问题的软件包缺失依赖的最简单方法是将 iterall 添加为我们项目的 package.json 的依赖。
🌐 The easiest solution to resolve missing dependencies of the buggy packages is to
add iterall as a dependency to our project's package.json.
你可以通过使用 pnpm add iterall 安装它,并且它将自动添加到你项目的 package.json 中。
🌐 You can do so, by installing it via pnpm add iterall, and will be
automatically added to your project's package.json.
"dependencies": {
...
"iterall": "^1.2.2",
...
}
解决方案 3
🌐 Solution 3
一种解决方案是使用 hooks 将缺失的依赖添加到包的 package.json 中。
🌐 One of the solutions is to use hooks for adding the missing
dependencies to the package's package.json.
一个例子是 Webpack Dashboard,它在 pnpm 上无法工作。现在问题已经解决,它可以在 pnpm 上正常工作了。
🌐 An example was Webpack Dashboard which wasn't working with pnpm. It has
since been resolved such that it works with pnpm now.
它曾经抛出一个错误:
🌐 It used to throw an error:
Error: Cannot find module 'babel-traverse'
at /node_modules/inspectpack@2.2.3/node_modules/inspectpack/lib/actions/parse
问题在于 babel-traverse 被用在了 inspectpack 中,而 inspectpack 又被 webpack-dashboard 使用,但在 inspectpack 的 package.json 中没有指定 babel-traverse。它在 npm 和 yarn 中仍然可以工作,因为它们生成的是扁平的 node_modules。
🌐 The problem was that babel-traverse was used in inspectpack which
was used by webpack-dashboard, but babel-traverse wasn't specified in
inspectpack's package.json. It still worked with npm and yarn because
they create flat node_modules.
解决方案是创建一个包含以下内容的 .pnpmfile.mjs:
🌐 The solution was to create a .pnpmfile.mjs with the following contents:
export const hooks = {
readPackage: (pkg) => {
if (pkg.name === "inspectpack") {
pkg.dependencies['babel-traverse'] = '^6.26.0';
}
return pkg;
}
}
创建 .pnpmfile.mjs 后,只需删除 pnpm-lock.yaml——无需删除 node_modules,因为 pnpm 钩子只影响模块解析。然后,重建依赖,应该就能正常工作了。
🌐 After creating a .pnpmfile.mjs, delete pnpm-lock.yaml only - there is no need
to delete node_modules, as pnpm hooks only affect module resolution. Then,
rebuild the dependencies & it should be working.