平铺的结构不是 node_modules 的唯一实现方式
Pnpm 的新用户们经常会问我关于 pnpm 创建的奇怪的 node_modules
结构。 为什么不是平铺的? 次级依赖去哪了?
我将默认这篇文章的读者已经熟悉了 npm 与 yarn 创建的平铺的
node_modules
。 如果你不明白为什么 npm3 需要开始在 v3 中使用平铺的node_modules
,你可以在这里找到一些背景知识 Why should we use 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
你可以在这里看到整个目录。
然后这一个node_modules
是你通过 pnpm 创建的得到的:
.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/express@4.17.1/node_modules/express。
OK,所以我们现在知道了 .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
! The second trick of pnpm's node_modules
structure is that the dependencies of packages are on the same directory level as the real location of the dependent package. 所以 express
的依赖不在 .pnpm/express@4.17.1/node_modules/express/node_modules/
而是在 .pnpm/express@4.17.1/node_modules/:
▾ node_modules
▾ .pnpm
▸ accepts@1.3.5
▸ array-flatten@1.1.1
...
▾ express@4.16.3
▾ 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。
- 包与其依赖被完美地组织在一起。
有 peer 依赖的包的结构更加复杂一些,但思路是一样的:使用软链与平铺目录来构建一个嵌套结构。