npm
npm 1.x / 2.x 时期
npm 1.x版本和2.x版本,在下载包时,采用的是嵌套依赖树的模式。比如项目(project)依赖于package1和package2,同时package1和package2都依赖于package3,那么最终下载的依赖树结构如下:
最终下载的
node_modules
结构如下:
project
- node_modules
--- package1
----- node_modules
------- package3
--- package2
----- node_modules
------- package3
其中,package3会被下载两次。这种嵌套依赖树的管理方式比较简单方便,但也会带来重复包太多的问题。同时,由于嵌套的层次未知,可能会带来访问路径太深的情况。
npm 3.x时期
npm从3.x版本开始,npm采用了扁平化依赖树的方式来管理包之间依赖。基于这种方式,下载包时,npm会将每个树节点的依赖提升根目录的 node_modules
下,出现相同包时,则按以下规则处理:
- 版本相同,则去重,保留一个。
- 版本不同,则保留先下载的包在根目录的
node_modules
下,将新版本的包下载在树节点下的node_mudules
下。以此来区分不同版本包的依赖。
例如下图的依赖树:
假设先解析下载的包为package1,则最终下载的 node_modules
结构如下:
project
- node_modules
--- package1
--- package3
--- package4@1.0.0
--- package2
----- node_modules
------- package4@2.0.0
基于此,npm很大程度上解决了包重复下载和包访问路径过深的问题。但扁平化依赖树算法的出现,也带来了一定的计算开销。同时,npm依赖node-semver
的包版本解析存在版本的不确定性,这会带来 npm install
时,不同的机器环境下安装的包版本会不一致。
npm v5时期
主要新增了 package-lock.json。该文件记录了 npm install
时,实际安装的包和依赖包的版本等信息。借助该文件,npm安装时的大概流程如下:
通过git版本控制改文件的一致性,可以达到不同机器环境,安装的包版本一致的效果。
yarn
yarn的出现,主要是针对npm各个时期发展中出现的不足,不断提出改进的方案,同时也促进了npm的不断优化。
特点
- 并行安装:npm安装包的时候,是以串行的方式进行安装;而yarn在装包的时候,采用的是并行的方式进行,相对之下,效率要提高很多。
- 离线模式:对于已经安装过的包,yarn会缓存起来,再次安装时,会直接从缓存中获取,借此特性可以不需要通过网络去离线装包。
- 版本锁定:为了防止拉取到不同的版本,Yarn在安装包的时候会生成
yarn.lock
文件 ,该文件记录了被安装的包的版本号。每次只要新增了一个模块,Yarn 就会更新这个文件。
版本
-
yarn classic:yarn的初始版本,其目录结构与npm类似,保留了
node_modules
目录,同时带来了并行安装、离线下载、版本锁定等功能。 -
yarn berry:yarn的新版本,摒弃了
node_modules
目录,会生成以下几个文件,其中cache
目录存放了依赖包的压缩文件,减少了包大小,.pnp
文件用于项目定位依赖的包在cache
中的哪个压缩文件中。unplugged
目录用于存放解压后的包,可以通过yarn unplug xx
进行解压对应的包。同时,新版本的yarn也支持自定义插件和.yml
的配置文件。.yarn/cache .yarn/unplugged .pnp
pnpm
pnpm动机
pnpm的出现是为了节省磁盘空间和提升项目的安装速度。npm和yarn的包管理形式是,每个项目都有自己的包目录,多个项目间会存在很多相同的包,大大浪费了磁盘的空间。pnpm实现该目的的方式主要是以下几点:
- 将包放到磁盘可寻址的空间中,每当包有新版本更新时,则另外储存版本的不同文件,相同文件不重复
- 当项目依赖相应的包时,pnpm通过硬链接的方式来链接到相应的依赖包,借此,不同的项目就可以共享相同的包,而不必每个项目都拷贝一份。
- 由于不必下载包到项目下,只是通过生成链接文件的方式来依赖包,随着磁盘中已有的共享包越来越多,pnpm构建项目的速度也会越来越快。
项目结构
pnpm构建后,依赖的包结构如下:
- package.json中直接依赖的包生成到
node_modules
下,并软链接(符号链接)到.pnpm
中对应的依赖包。 - 生成
.pnpm
目录,目录下存放每个依赖包的硬链接和符号链接的关系。硬链接主要是依赖包跟磁盘的联系,符号链接用来指定依赖包直接的联系。
总结
npm、yarn、pnpm在各自的版本迭代中,不断完善缺陷,加入新的特性。随着版本的迭代优化,每个包管理器之间会有相互借鉴和改进的情况,下表是依据个人目前的了解对各个特性的对比总结。
npm | yarn | pnpm | |
---|---|---|---|
嵌套依赖 | v1.0 / v2.0 | × | × |
扁平化依赖 | v3.0 | yarn classic | × |
依赖包压缩 | × | yarn berry | × |
版本锁定 | v5.0 | √ | √ |
并行下载 | × | √ | × |
离线下载 | × | √ | × |
共享磁盘包 | × | × | √ |