参考:www.dhiwise.com/post/pnpm-v…
带着疑问往下看:
- 软连接和硬链接有什么区别?
- 循环依赖怎么解决?maples7.com/2016/08/17/…
es6.ruanyifeng.com/#docs/modul…
- 幽灵依赖是什么?版本依赖管理(antd 与 @ant-design/cssinjs :github.com/ant-design/…
一、npm/yarn
npm/yarn install之后,首先会构建依赖树,然后针对每个节点下的包,会经历下面四个步骤:
-
将依赖包的版本区间解析为某个具体的版本号
-
下载对应版本依赖的 tar 包到本地离线镜像
-
将依赖从离线镜像解压到本地缓存
-
将依赖从缓存拷贝到当前目录的 node_modules 目录
(1)早年npm1、npm2 中呈现出的是嵌套结构
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
存在什么问题:
-
依赖层级太深,会导致文件路径过长的问题,尤其在 window 系统下。
-
大量重复的包被安装,文件体积超级大。比如跟
foo同级目录下有一个baz,两者都依赖于同一个版本的lodash,那么 lodash 会分别在两者的 node_modules 中被安装,也就是重复安装。 -
模块实例不能共享。比如 React 有一些内部变量,在两个不同包引入的 React 不是同一个模块实例,因此无法共享内部变量,导致一些不可预知的 bug。
facebook的yarn首次采用了 扁平化结构去解决,嵌套过深的问题。npm3也开始学习yarn的思路。
node_modules
├─ foo
| ├─ index.js
| └─ package.json
└─ bar
├─ index.js
└─ package.json
扁平化的缺点:
-
依赖结构的不确定性。juejin.cn/post/693204…
-
扁平化算法本身的复杂性很高,耗时较长。
-
幽灵依赖:项目中可以非法访问没有声明过依赖的包。
二、硬链接(Hard Link)和 符号链接(Symbolic Link, 又称软连接)
参考:
简单说明:www.runoob.com/note/29134
详细场景:www.51cto.com/article/667…
硬连接(Hard Link)
硬连接指通过索引节点inode 来进行连接。在 Linux 的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在 Linux 中,多个文件名指向同一索引节点是存在的。比如:A 是 B 的硬链接(A 和 B 都是文件名),则 A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号相同,即一个 inode 节点对应两个不同的文件名,两个文件名指向同一个文件,A 和 B 对文件系统来说是完全平等的。删除其中任何一个都不会影响另外一个的访问。
硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。
硬链接存在 2 个限制:
不允许用户给目录创建硬链接,即:用户不可以,操作系统可以(比如:每个目录下的 . 和 ..);
只有在同一个文件系统中的文件,才能创建硬链接,也就是说:不能跨文件系统。
为了克服硬链接的 2 个限制,软链接被引入进来了!
符号链接(Symbolic Link, 又称软连接)
软链接文件的内容是一个文本字符串,存储的是目标文件(即:链接到的文件)的路径名。
软链接文件有类似于 Windows 的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。比如:A 是 B 的软链接(A 和 B 都是文件名),A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号不相同,A 和 B 指向的是两个不同的 inode,继而指向两块不同的数据块。但是 A 的数据块中存放的只是 B 的路径名(可以根据这个找到 B 的目录项)。A 和 B 之间是“主从”关系,如果 B 被删除了,A 仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。
三、pnpm
(一)节省磁盘空间(全局的仓库管理)
使用 npm 时,依赖每次被不同的项目使用,都会重复安装一次。 而在使用 pnpm 时,依赖会被存储在内容可寻址的存储中,所以:
- 如果你用到了某依赖项的不同版本,只会将不同版本间有差异的文件添加到仓库。 For instance, if it has 100 files, and a new version has a change in only one of those files,
pnpm updatewill only add 1 new file to the store, instead of cloning the entire dependency just for the singular change. - 所有文件都会存储在硬盘上的某一位置。 当软件包被被安装时,包里的文件会硬链接到这一位置,而不会占用额外的磁盘空间。 这允许你跨项目地共享同一版本的依赖。
因此,您在磁盘上节省了大量空间,这与项目和依赖项的数量成正比,并且安装速度要快得多!
(二)提高安装速度
- 依赖解析(resolving)。 仓库中没有的依赖都被识别并获取到仓库。
- 目录结构计算。 The
node_modulesdirectory structure is calculated based on the dependencies. - 链接依赖项(linking)。 All remaining dependencies are fetched and hard linked from the store to
node_modules.