likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 50

原文链接:Flat node_modules is not the only way,2020.05.27,by Zoltan Kochan

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

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

我假设读者已经熟悉 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_modulesexpress 的所有依赖在哪里?

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

这里: node_modules/.pnpm/express@4.17.1/node_modules/express

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

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

我们称之为虚拟存储目录(virtual store directory)。

译注:pnpm 的虚拟存储目录最初是 .registry.npmjs.org///node_modules/ 方式,后来才改成上述结构。因为 .pnpm 具有标识意义,类似于项目中的 .vscode、.github 的作用

这种扁平结构避免了由 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/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 的依赖放在上一级可以避免循环符号链接。

所以你可以看到,尽管 pnpmnode_modules 结构一开始看起来不寻常:

  • 它完全兼容 Node.js
  • 包和它们的依赖关系又能被很好地分组

译注:这就是 pnpm 非常巧思的地方,利用了 Node.js 查找依赖时的寻址策略(查找当前目录或上层目录中的 node_modules 目录,以此类推),配合符号链接:既解决了 npm 扁平化带来的间接依赖暴露问题又做到了依赖分组,同时借助全局存储(global store)大大节省了硬盘空间。

对于具有 peer dependencies 的包,结构稍微复杂些,但思路是相同的:使用符号链接创建一个嵌套并具有扁平化的目录结构。

转载自:https://juejin.cn/post/7255309681547952185
评论
请登录