现代包管理器
npm
npm
是最早的现代包管理器,使用npm init -y
就可以快速创建一个npm
管理的项目
在npm2
及之前,依赖的管理都是嵌套结构的,就是安装的包如果有其他依赖,会直接在node_modules
的该包的目录下在创建一个node_modules
,这样就导致了同一个依赖可能会被安装在不同的目录之下非常多次,大量占据了空间。而且在windows
之下还会有路径过长的问题。
在npm3
之后才解决了这个问题,但是在此之前出现了解决方案--yarn
npm i是在package.json允许的范围内尝试更新依赖,并重新写入.lock文件中. npm ci是严格按照.lock文件中指定的依赖版本安装. 在测试环境和开发环境,使用这命令能更稳定的运行项目.
yarn
yarn
对依赖的处理是平铺的,即将所有的依赖都提升到顶层来,而不是一层层的嵌套。但是这不是完全没有嵌套了,如果一个包有多个依赖的话,只会提升一个,其他的还是会出现嵌套依赖的情况。
pnpm
不管是npm
还是yarn
依赖平铺解决方案,虽然解决了一部分问题,但是也有其他的问题。
平铺结构导致一些并没有在dependencies
中声明的依赖,却依然可以在代码中require
引入,这会带来安全问题,比如有个包原来依赖了某个其他包,等到不依赖这个包之后,代码可能就会出现问题了,因为代码中并没有显示声明依赖,而是通过其他包来安装的,这个时候就会有问题。
其次就是多个版本的依赖只会提升一个包到顶层,并没有完全解决空间浪费的问题。
所以pnpm
就出现了
pnpm
内部通过链接 的方式来管理文件:所有的依赖都保存到全局仓库store内,然后通过硬链接
到本地store(node_modules/.pnpm
),依赖和依赖之间通过软链接
来管理。这样就解决了空间浪费问题,也提高了安装的效率
链接是操作系统提供的,分为硬链接Hard Link和软链接Symbolic Link两种。
操作系统中文件的读取是通过索引节点号inode index的,一般一个文件名指向一个inode index,但是也会存在多个文件名指向一个inode index的情况
硬链接:通过索引节点来链接文件,硬链接就是多个文件名指向一个inode index,一般用来防止误删操作,因为即使删掉一个,对应的inode index并不会改变。
软链接:软链接中文件实际是一个文本文件,包含其他文件的位置信息,相当于指针
同时pnpm
还支持monorepo
,体现在子命令的功能之上,如pnpm add XX -r
,那么所有的package
都会安装此依赖
monorepo是一种管理方案,将多个子项目放在同一个仓库下,每个子项目有自己的package
依赖管理
npm/yarn在执行install
命令之后,包是如何安装到node_modules
下的:
- 首先解析依赖包的版本区间,具体到某个版本包
- 下载对应的版本依赖的tar包到本地离线镜像
- 将依赖从离线镜像解压到本地缓存
- 最后将依赖从缓存中拷贝到当前目录的
node_modules
中
lock
文件的作用:确保每次install
之后都是相同结构的node_modules
目录,主要是为了解决版本不确定以及扁平化依赖管理
带来的依赖结构不确定性:
- 版本不确定
- 依赖结构不确定:扁平化会提升依赖到顶层,但是多个版本的话只会提升一个且提升的并不确定,根据在
package
文件中声明的位置来判断
依赖类型
dependencies
:项目运行时依赖,即项目运行时的最小依赖.devDependencies
:项目开发时依赖.开发时的依赖,比如一些types定义/测试工具/构建工具等.peerDependencies
:项目运行时依赖,但是项目运行时依赖失败时,项目会报错,影响项目运行.这个一般是放一些不希望放到dependencies
中的,但是项目又需要使用的依赖.optionalDependencies
:可选依赖,通常用于提供额外的优化,即使安装失败也不会影响后续操作.bundledDependencies
:用于指定需要一起打包发布的依赖,一般不用.
虽然常见的web项目,都是将代码和依赖一起打包的,不区分
dependencies
和devDependencies
,但是最好还是最好语义化的区分.