扁平化的 node_modules 不是唯一的方式
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
结构乍一看似乎不寻常
- 它完全与 Node.js 兼容
- 包与其依赖项很好地分组在一起
对于具有对等依赖项的包,该结构稍微 更复杂,但理念是相同的:使用符号链接来创建具有扁平化目录结构的嵌套结构。