前端包管理工具 npm pnpm yarn 以及 幽灵依赖
在介绍这三种包管理工具之前,先来看几个其他的概念
幽灵依赖的出现和 npm 的发展
早在 npm 的 v1 和 v2 版本中,npm 的包管理工具是基于嵌套结构来管理包的,也就是说,如果一个项目依赖了某个包,那么这个包就会被下载到这个依赖项目的 node_modules 文件夹中。打个比方,我们有如下的包结构,我们在安装依赖的时候会安装依赖C,在安装依赖A的时候还会安装依赖C,导致依赖的重复安装,并且在解析依赖的时候,容易造成依赖地狱,比如A->C->D->B,依赖路径无比长。
例如下图的结构
这个时候我们可以发现,依赖的路径会随着子依赖的增多而变的路径过长,一般路径地址长度处理为 255 个字符,所以当依赖的包越来越多的时候,就会出现路径过长无法解析的的问题。
为了解决这个问题,npm 在 v3 版本中引入了扁平化依赖的机制,也就是说,如果一个项目依赖了某个包,那么这个包就会被下载到这个依赖项目的 node_modules 文件夹中,而不是子依赖的 node_modules 文件夹中。 这样就会解决 v1 和 v2 版本中出现的路径过长解析不了的问题,但是也会出现新的问题,那就是幽灵依赖的问题。
npm v3 的结构如下图
这里存在 npm v3 扁平化处理的规则:
- 当遇到两个依赖的包版本不一致的时候,首先尝试兼容:例如一个需要 ^1.0.0,另一个需要 ~1.0.0),那么 npm 会尝试找到一个满足所有需求的单一版本,并将其安装在顶层 node_modules 中。
- 如果兼容失败,则会分离安装,比如(比如一个是 1.x 范围,而另一个是 2.x 范围)那么 npm 将为每个版本分别创建单独的实例,并保持原有的嵌套层级关系。也就是说,在这种情况下,不会进行依赖提升,而是按照原始的依赖声明保留各自的版本。
- 锁定依赖:npm 会为每个依赖包创建一个 package-lock.json 文件,用于锁定依赖的版本。这样在安装依赖的时候,就会按照 package-lock.json 文件中的版本进行安装,而不是按照 package.json 文件中的版本进行安装。保证不同环境下安装的依赖相同。
幽灵依赖
幽灵依赖是指在 node_modules 文件夹中,存在一些包,但是这些包并没有在 package.json 文件中被声明为依赖。这些包是项目依赖包的子依赖,由于扁平化依赖的机制,这些子依赖被下载到了父依赖的 node_modules 文件夹中,但是这些子依赖并没有被声明为依赖,所以这些包就被称为幽灵依赖。
幽灵依赖会导致当我们项目的依赖的某个包的版本发生变化的时候,子依赖的版本也会发生变化,这样就会导致项目依赖的包的版本不一致,从而导致项目无法正常运行。或者包发生了破坏性更新,把某个包的依赖删除了,我们的项目使用了这个幽灵依赖,就会产生报错。
可以看到业务项目依赖包A,包A依赖包2、3、4,我们在项目中没有声明安装依赖包2、3、4,但是包A依赖包2、3、4,所以包2、3、4就被安装到了业务项目的 node_modules 文件夹中,我们在我们的代码中是可以直接使用这些包的,这些包就是幽灵依赖。
dependencies 和 devDependencies 和 peerDependencies
在平时的业务项目中,大部分在 package.json 文件中,会看到 dependencies 和 devDependencies。
dependencies
dependencies 是项目依赖的包,这些包是项目运行所必需的。就是我们在项目中使用 import 或者 require 导入的包。
devDependencies
devDependencies 是项目开发依赖的包,这些包是项目开发所必需的。在项目运行的时候,这些包不会被安装到项目中。比如 eslint 和 prettier 这些包,在项目运行的时候,这些包不会被安装到项目中。
peerDependencies
peerDependencies 对等依赖。这个在发布 npm 包的时候,会用到。打个比方,我们的包使用的是某个插件或者依赖的2.0.0版本,这个版本推出一系列新的 api ,我们可以通过这个对等依赖要求依赖我们组件的项目要使用这个包的 2.0.0 版本。这意味着你的包需要另一个包并且指定版本的存在才能正常工作。告诉使用我们包的项目需要再下载哪些的依赖。
yarn
什么是 yarn
yarn 也是一款前端包管理工具。他比 npm 更快,npm是逐个下载依赖,而 yarn 是并行下载依赖,所以 yarn 更快。yarn 还使用存储包的机制。Yarn 允许在没有网络连接的情况下进行安装和构建,只要之前已经下载过依赖项并缓存在本地。但是随着 npm 的更新,npm 和 yarn 的差距越来越小,npm 也支持了并行下载依赖。
pnpm
pnpm 解决了 npm 的幽灵依赖问题,我们来看是如何解决的,npm 为了解决路径长的问题,将依赖拍平处理,导致有些包被提到了顶层的 node_modules 文件夹中,解决幽灵依赖的问题,只要这些包不在顶层就可以了。 pnpm 既要使用扁平化的处理方式,又不能提到顶层。那就将所有的依赖放到一个文件夹 .pnpm 中,然后通过软链接的方式,将依赖链接到项目中。这样我们项目直接的依赖还是放在顶层,通过软连接的方式去链接到 .pnpm 文件夹中。这些依赖的子依赖还会保持原来的依赖树关系,只是都使用软连接到 .pnpm 文件夹中。
符号链接(symlinks)是一种文件系统对象,它指向另一个文件或目录,作为目标路径的快捷方式。符号链接可以分为两种类型:硬链接和软链接(也称为符号链接)。这里我们主要讨论的是软链接,因为它是在包管理器如 pnpm 中用于优化依赖安装的主要机制。
软链接的特点
- 跨文件系统:软链接可以跨越不同的文件系统,即它可以指向位于不同磁盘分区上的文件或目录。
- 轻量级:创建一个软链接只需要很小的存储空间,因为它只包含目标路径的信息。
- 透明性:对于大多数应用程序来说,访问软链接就像访问实际文件或目录一样。例如,如果一个程序打开了一个文件的软链接,它会直接读取到原始文件的内容。
- 可删除性:如果删除了软链接本身,不会影响到被链接的目标文件;但是,如果删除了目标文件,则该软链接将变得无效,成为“悬空链接”。
欢迎关注我的公众号“前端趴菜成长记”,原创技术文章第一时间推送。