pnpm
前置知识
符号链接/软连接(symbolic link)
以下解释来自百度
符号链接(软链接)是一类特殊的文件, 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用。[1] 符号链接最早在4.2BSD版本中出现(1983年)。今天POSIX操作系统标准、大多数类Unix系统、Windows Vista、Windows 7都支持符号链接。Windows 2000与Windows XP在某种程度上也支持符号链接。
其实也可以理解为快捷方式,在windows 下面右键文件可以创建,也可以使用linux 命令
// ln [参数][源文件或目录][目标文件或目录]
// -s 代表创建软连接,不加代表创建硬连接
ln -s test.text test_symbolic_link.txt
硬链接
以下解释来自百度
硬链接(hard link,也称链接)就是一个文件的一个或多个文件名。再说白点,所谓链接无非是把文件名和计算机文件系统使用的节点号链接起来。因此我们可以用多个文件名与同一个文件进行链接,这些文件名可以在同一目录或不同目录。
ln -s test.text test_hard_link.txt
使用 ls -i
可以看到文件的(文件系统中的物理索引(也称为inode)
硬链接和源文件的inode 是同一个
硬链接和软链接的区别
软链接:
- 1.软链接,以路径的形式存在。类似于Windows操作系统中的快捷方式
- 2.软链接可以跨文件系统(FAT32、NTFS...) ,硬链接不可以
- 3.软链接可以对一个不存在的文件名进行链接
- 4.软链接可以对目录进行链接
硬链接:
- 1.硬链接,以文件副本的形式存在。但不占用实际空间(存疑??实际上我看到还有占了空间)
- 2.不允许给目录创建硬链接
- 3.硬链接只有在同一个文件系统中才能创建
内容可寻址存储(Contenct-Addressable Store,CAS)
是一种存储信息的方式,与此对应的还有固定内容存储(FAS)
内容可寻址存储,也称为内容寻址存储或缩写为CAS,是一种存储信息的方式,因此可以根据其内容而不是其位置来检索信息。它已被用于高速存储和检索的固定内容,如存储,符合政府规定的文件。内容可寻址存储类似于内容可寻址内存。 具体就不展开了,只要理解两个店,他是一种存储信息的方式,常使用加密哈希函数从文档生成的摘要,以标识存储系统中的该文档
使用pnpm的好处
节省磁盘空间
当使用npm时,如果你有100个项目使用一个依赖关系,你将有100份该依赖关系的副本保存在磁盘上。使用pnpm,该依赖关系将被存储在一个内容可寻址的存储器中,因此。
如果你依赖不同版本的依赖关系,只有不同的文件会被添加到存储区。例如,如果它有100个文件,而新版本只对其中的一个文件进行了修改,那么pnpm update将只向存储区添加一个新文件,而不是仅仅为了这个单一的修改而克隆整个依赖关系。
所有的文件都保存在磁盘上的一个地方。当软件包被安装时,它们的文件被硬链接到那个地方,不消耗额外的磁盘空间。这使得你可以在不同的项目中共享同一版本的依赖关系。
因此,你可以在磁盘上节省大量与项目和依赖关系数量成比例的空间,而且你的安装速度也会快很多
以上摘自官网,我理解是pnpm比yarn、npm快的原因就是所有项目的依赖在一个store, 如果这个store里面有的话,则直接用,没有的话则从远程下载,这是快和节省空间的原因
那么问题来了这个store是在哪里,下面会给出答案
加快安装速度
- 依赖性解析。所有需要的依赖被识别,并被提取到store。
- 目录结构计算。根据依赖关系计算 node_modules 目录结构。
- 链接依赖项。所有剩余的依赖被获取并从store硬链接到node_modules
pnpm 究竟怎么管理依赖的
首先我们明白几个点, node_modules 下面究竟有哪些部分。以下以一个nextjs demo举例
pnpm 的
node_modules
布局使用符号链接来创建依赖项的嵌套结构
pnpm虚拟目录
以平铺(是不是和npm3.x有点像,注意是平铺在.pnpm下面)的形式项目所有的依赖包(包括间接依赖,每个依赖包都可以通过.pnpm/<name>@<version>/node_modules/<name>
路径找到实际位置
如 .pnpm/array-name@2.1.0/node_modules/array-union
还是以官网的例子,真实demo中稍微比这复杂(比如上图有些包名为何是两个包甚至多个包名合起来的??)。 假设我们直接项目安装了foo@1.0.0
,bar@1.0.0
和qar@2.0.0
是间接依赖
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo // 直接依赖。符号链接到./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ ├── bar -> <store>/bar // 包中的每个文件都硬链接到pnpm store中的对应文件,即<pnpm store path>/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar // 间接依赖qar,符号链接到../../qar@2.0.0/node_modules/qar
├── foo@1.0.0
│ └── node_modules
│ ├── foo -> <store>/foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
└── qar@2.0.0
└── node_modules
└── qar -> <store>/qar
再来看这个图就很好理解了
另找了一下直接依赖下面的bin文件,看不太懂
我觉得这BEFE这个总结很好
总结:pnpm使用符号链接Symbolic link(软链接)来创建依赖项的嵌套结构,将项目的直接依赖符号链接到
node_modules
的根目录,直接依赖的实际位置在.pnpm/<name>@<version>/node_modules/<name>
,依赖包中的每个文件再硬链接(Hard link)到.pnpm store
pnpm store
pnpm store path // pnpm store的存储路径
终于看到了庐山真面目了,原来真正的文件在这里。注意
- Content-addressable store 使用 pnpm store path 可查看
- Virtual store 也就是.pnpm 目录
pnpm install 安装的时候, 你会看到以下提示
packages are hard linked form the contenct-addressable store to the virtual store
关于pnpm store 的一些命令
- pnpm status
查看 store 中已修改的包。
- pnpm add
功能上等同于 pnpm add
,不同之处在于它只把包加入存储中,且没有修改存储外的任何项目或文件。
- pnpm prune
删除未被引用的包
重点是这个命令,因为store随着项目的增多,肯定会越来越多,那么如何保证不庞大呢。 删除未被引用的包, 因为
pnpm install
期间,包foo@1.0.0
被更新为foo@1.0.1
。 pnpm 将在存储中保留foo@1.0.0
,因为它不会自动除去包。 如果包foo@1.0.0
没有被其他任何项目使用,它将变为未引用。 运行pnpm store prune
将会把foo@1.0.0
从存储中删除 。
但还是要注意不要经常清理(我运行的时候,电脑风扇会响)
运行
pnpm store prune
是无害的,对您的项目没有副作用。 如果以后的安装需要已经被删除的包,pnpm 将重新下载他们。 最好的做法是pnpm store prune
来清理存储,但不要太频繁。 有时,未引用的包会再次被需要。 这可能在切换分支和安装旧的依赖项时发生,在这种情况下,pnpm 需要重新下载所有删除的包,会暂时减慢安装过程
- 更多命令
pnpm如何管理 peer dependencies
还是以官网的例子来说,
foo 有两组不同的依赖项, 一个是baz@1.0.0,另外一个是baz@1.1.0,
- foo-parent-1
- bar@1.0.0
- baz@1.0.0
- foo@1.0.0
- foo-parent-2
- bar@1.0.0
- baz@1.1.0
- foo@1.0.0
如果他们有peer,就有两组不同的
node_modules
└── .pnpm
├── foo@1.0.0_bar@1.0.0+baz@1.0.0
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ ├── baz -> ../../baz@1.0.0/node_modules/baz
├── foo@1.0.0_bar@1.0.0+baz@1.1.0
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ ├── baz -> ../../baz@1.1.0/node_modules/baz
├── bar@1.0.0
├── baz@1.0.0
├── baz@1.1.0
这里的依赖包名就是我之前提出疑问的,为啥包名像是组合起来,难道是因为peer dependencies,不过我还不太明白,因为我看了并没有找到peer dependencies
附[V8.0.0]发布(github.com/pnpm/pnpm/r…)
怎么管理monoRerpo
如果 bar依赖于 foo@1.0.0
pnpm 支持 workspace 协议 workspace:
。 当使用此协议时,pnpm 将拒绝解析除本地 workspace 包含的 package 之外的任何内容。 因此,如果您设置为 "foo": "workspace:2.0.0"
时,安装将会失败,因为 "foo@2.0.0"
不存在于此 workspace 中。
看下umi定义的workspace
一些问题
为什么不直接创建到全局的symbolic link, 而是使用hard link
有没有觉得硬链接是多余的,事实官网也说可行,但是有bug
硬链接好像也会占用空间?
空间大小和源文件一样 Why do hard links seem to take the same space as the originals?](unix.stackexchange.com/questions/8…)
上图的意思是
- 创建 1 2文件夹
- 为1文件夹新建a文件,并且往a文件输入 “foo”
- 为2文件夹新建a文件,并且往a文件输入 “bar”
- 为1文件夹的a文件创建软连接b
- 创建1文件夹b文件的硬链接到2文件夹的b文件
分别用 ls -li 1 2
和 du -h 1 2
查看 文件夹 1 2 ,可以看到 两个命令统计出不一样的结果,第二个命令4.0k就是文件a的大小
- 1代表 inode
- 2代表读写权限
- 3代表硬链接数
- 4文件所有者
- 5文件所属组
- 6文件大小
- 7修改时间
- 8文件名
在实际项目中, .pnpm 被组合起来的包名究竟是啥意思,是因为peers dependencies么
对比yarn、npm
功能对比
竞争
Yarn
Yarn
在 v3.1 添加了 pnpm
链接器。 因此 Yarn
可以创建一个类似于 pnpm
创建的 node_modules
目录结构。
此外,Yarn
团队计划实现内容可寻址存储,以提高磁盘空间效率。
npm
npm
团队决定也采用 pnpm
使用的符号链接的 node_modules
目录结构(相关 RFC)。
TODO
-
pnpm.lock文件是怎么组织的
-
npm 或者yarn 转 pnpm
-
.pnpm 被组合起来的包名究竟是啥意思,是因为peers dependencies么
-
可交互式升级