包管理器 pnpm 中的软链与硬链

3,307 阅读4分钟

平时都是使用 npmyarn 来作为前端项目的包管理,最近突然听到别人说 pnpm 比较流行,其中的软链和硬链设计很秀。

本文就是记录下自己查看 pnpm 中包管理中软链和硬链的设计。

首先大家可以先自行了解下什么事软链和硬链。参考一下这篇文章

安装

然后开始安装 pnpm,具体安装方法可以看它的官网。网址在这里

创建项目

好了,然后我们就创建一个项目

cd ~/Documents

mkdir pnpm-demo

cd pnpm-demo

pnpm init

当然你会发现 pnpm init 实际和 npm init,没有什么区别,那这就对了, pnpm 主要是用来管理npm 包的,实际的名利和 npm 差距不大。

安装一个包

来来来,这回我们要用 pnpm 安装一个包了~~~ 就 vue

pnpm install vue

看是不是和 npm 一样的命令。

安装成功之后我们查看一下项目中的 node_modules 目录。

image.png

我用的是vscode编辑器,大家注意到 vue 文件后面是不是有个回车的icon,这个代表当前这个文件是一个软连接文件。

可以通过命令查看一下,可以看到 node_modules/vue 实际是 .pnpm/vue@2.6.14/node_modules/vue 的软连接

image.png

我们再看下 .pnpm/vue@2.6.14/node_modules/vue 中的内容,就是 vue 的包。

image.png

也就是说当你的项目中使用了 import Vue from 'vue' 的时候,根据 node 的路径分析规则,他会去 node_modules/vue 中查找,而 node_modules/vue 实际是 .pnpm/vue@2.6.14/node_modules/vue 的软连接

好,这时候你可能会有疑问:

  • 为什么不是 .pnpm/vue@2.6.14 来存放 vue,然后软链到 .pnpm/vue@2.6.14,反而要用 .pnpm/<packagename>@<version>/node_modules/<packagename> 的形式

  • 如果 vue 有其他以来怎么办

补充个小Tip

软链接的特性决定了在执行过程中他的文件路径和软连接地址是不配匹配的

举个例子

// <root>/script/index.js

console.log(process.cwd());
console.log(__dirname);
console.log(__filename);

# 创建上述文件的一个软件链接

ln -s script/index.js index-ls.js

# 执行软链接文件

nodre index-ls.js
# 返回结果
$ <root>
$ <root>/script
$ <root>/script/index.js
## 真正执行的实际上是 index.js

回到正文

那我们接着安装一个 react 吧,反正现在前端项目不是 react 就是 vue

pnpm install react

image.png

安装完可以看到 node_modules 下面多了 react,但是 .pnpm 里面却多了好多包。

再看下 .pnpm/react@17.0.2 下面就知道, pnpm为什么会这么设计了。

image.png

以 react 举例 .pnpm/react@17.0.2/node_modules/ 下面除了有react自己,还有react依赖的npm包。

这样的结合软链、包查找规则以及防止文件路径过长来设计的

还是要举例说明下

  • 项目中引用了 react ,依赖管理的规则会去 node_modules 里面查找react
  • 当找到 reat 之后,在代码执行过程中又发现 react 依赖了 object-assign
  • 依赖管理的规则会查找当前目录下的 node_modules,如果没有找到会查找父目录的 node_modules 一直找到根目录为止
  • node_modules/react 实际是 .pnpm/react@17.0.2/node_modules/react 的软链,它下面是没有 node_modules 的,那么根据规则,他会一直往上查找,直到找到 .pnpm/react@17.0.2 下的 node_moduels 中有 object-assign
  • 如果 object-assign 也会按照上面的流程执行

这就是 pnpm 如何根据软链特性以及依赖查找的规则实现一个巧妙的设计,npm包路径的长度最长也就是.pnpm/<packagename>@<version>/node_modules/<packagename>

可能说的比较饶,大家可以自行操作一下

pnpm 的硬链

上面可以看到 pnpm 使用软链达到一个巧妙的实际

在 pnpm 中还使用了硬链来实现快速的安装

我们查看下 node_modules/.pnpm/vue@2.6.14/node_modules/vue/package.json 这个文件

image.png

可以详细的看到这个文件的信息

索引节点 inode 为 st_ino=18046034

硬链 st_nlink=3

也就是文件索引的对应着系统上 3个地址,了解过上面说的硬链与软链就应该知道是什么意思。

find ~ -inum 18046034
# /Users/xxxxx/.pnpm-store/v3/files/b2/2c30605a327a1c3cdc0003e8b0d1d136ebf92bad4c3a7997c3849041cebfd75514cf18f459a77968b366b1d3e2353de6b52326cfde7955e1c7e9ff7b9996c3

上面的命令可以查找文件地址对应的文件inode18046034

如果你再新建个项目,再次安装 vue@2.6.14 然后重新查看node_modules/.pnpm/vue@2.6.14/node_modules/vue/package.json 你会返现 st_nlink=4

这说明如果你安装过一次之后,以后每次安装都会硬链一个到你的项目中,这比你原先缓存的复制的方式块多了。但有个缺点就是修改都是同步的,也就是说你修改当前项目中的node_modules会影响其他项目。