跳至主要内容

扁平化的 node_modules 不是唯一的方式

·阅读时长 3 分钟

pnpm 的新用户经常问我关于 pnpm 创建的 node_modules 的奇怪结构。为什么它不是扁平化的?所有子依赖项在哪里?

我将假设本文的读者已经熟悉 npm 和 Yarn 创建的扁平化 node_modules。如果你不明白为什么 npm 3 必须从 v3 开始使用扁平化的 node_modules,你可以在 为什么我们要使用 pnpm? 中找到一些历史记录。

那么为什么 pnpm 的 node_modules 不寻常呢?让我们创建两个目录,并在其中一个目录中运行 npm add express,在另一个目录中运行 pnpm add express。以下是你在第一个目录的 node_modules 中获得的顶部内容

.bin
accepts
array-flatten
body-parser
bytes
content-disposition
cookie-signature
cookie
debug
depd
destroy
ee-first
encodeurl
escape-html
etag
express

你可以在这里查看整个目录 这里.

这是你在 pnpm 创建的 node_modules 中获得的内容

.pnpm
.modules.yaml
express

你可以在这里查看它 这里.

那么所有依赖项在哪里?node_modules 中只有一个名为 .pnpm 的文件夹和一个名为 express 的符号链接。好吧,我们只安装了 express,所以这是你的应用程序必须访问的唯一包

阅读更多关于为什么 pnpm 的严格性是一件好事 这里

让我们看看 express 内部是什么

▾ node_modules
▸ .pnpm
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md
.modules.yaml

express 没有 node_modules?所有 express 的依赖项在哪里?

诀窍是 express 只是一个符号链接。当 Node.js 解析依赖项时,它使用它们的真实位置,因此它不会保留符号链接。但是你可能会问,express 的真实位置在哪里?

这里:node_modules/.pnpm/[email protected]/node_modules/express.

好的,所以现在我们知道 .pnpm/ 文件夹的用途。.pnpm/ 以扁平化的文件夹结构存储所有包,因此每个包都可以在一个以这种模式命名的文件夹中找到

.pnpm/<name>@<version>/node_modules/<name>

我们称之为虚拟存储目录。

这种扁平化的结构避免了由 npm v2 创建的嵌套 node_modules 引起的路径过长问题,但与 npm v3,4,5,6 或 Yarn v1 创建的扁平化 node_modules 不同,它保持了包的隔离。

现在让我们看看 express 的真实位置

  ▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md

这是骗局吗?它仍然缺少 node_modules!pnpm 的 node_modules 结构的第二个诀窍是,包的依赖项与依赖包的真实位置位于同一目录级别。因此,express 的依赖项不在 .pnpm/[email protected]/node_modules/express/node_modules/ 中,而是在 .pnpm/[email protected]/node_modules/

▾ node_modules
▾ .pnpm
[email protected]
[email protected]
...
[email protected]
▾ node_modules
▸ accepts
▸ array-flatten
▸ body-parser
▸ content-disposition
...
▸ etag
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md

express 的所有依赖项都是指向 node_modules/.pnpm/ 中相应目录的符号链接。将 express 的依赖项向上移动一级可以避免循环符号链接。

所以正如你所看到的,即使 pnpm 的 node_modules 结构乍一看似乎不寻常

  1. 它完全与 Node.js 兼容
  2. 包与其依赖项很好地分组在一起

对于具有对等依赖项的包,该结构稍微 更复杂,但理念是相同的:使用符号链接来创建具有扁平化目录结构的嵌套结构。