npm
什么是npm?
npm(Node Package Manager),是一个NodeJS包管理和分发工具,虽然非官方,但是目前已经是Node.js的默认包管理工具,安装nodejs时会自动安装npm。
什么是包管理工具?
包管理工具(或称为包管理器)是用于管理软件包或库的一种工具。在编程和开发环境中,这些包可以是代码库、框架、插件、工具或其他可重用组件,它们被封装成易于安装、配置和管理的包。
包管理工具提供了许多功能,包括自动处理项目依赖关系、支持跟踪依赖项和版本、安装、卸载、更新和升级包等。它们通常还提供了命令行工具(CLI)和图形用户界面(GUI),以方便用户使用。
对于开发者而言,包管理工具可以大大简化项目的构建、配置和维护过程。它们使得开发者能够轻松地引入和管理外部库和依赖项,从而加快开发速度,提高代码质量,并减少重复造轮子的工作。
其实不止是js有npm,其它语言也有自己的包管理工具。
语言 | 包管理工具 |
---|---|
NodeJS | npm/yarn/pnpm |
Java | Maven |
Python | Pip |
node_modules
使用npm安装包后,我们下载的包会被放到node_modules目录下。
安装方式 | ||
---|---|---|
本地安装(默认) | 将包放到当前项目根目录的./node_modules中 | 一般是当前项目使用的一些包 |
全局安装(--global) | 将包放在全局,一般是/user/local中或NodeJs的位置 | 全局使用的包,可能是一些工具,比如yarn |
npm的包版本管理机制
为什么需要npm来管理包版本?
npm所管理的包非常的庞大,同一个代码包还会有非常多的版本,而且包与包之间可能还存在相互依赖的关系。所以我们需要包管理工具来帮助我们快速安装合适的包。
如下图,一个项目往往会依赖数量极其庞大的包。这些包不定期还会升级版本,如果需要的话,npm还会自动帮我们追踪版本,实现自动版本升级。
npm配置文件package.json
初始化项目时,可以使用如下命令生成配置文件package.json。
csharp
npm init -y
配置文件常见属性:
属性 | 作用 |
---|---|
name | 项目名称【必填】 |
version | 项目版本【必填】 |
description | 项目描述信息 |
author | 项目作者 |
license | 开源协议 |
private | 项目是否私有 |
main | 项目入口 |
script | 用于配置脚本命令 |
dependencies | 全局依赖,不分环境 |
devDependencies | 开发环境才使用的依赖 |
peerDependencies | 对等依赖。 |
repository | 项目代码仓库地址 |
对等依赖的作用:
- 减小打包体积:例如使用react开发的组件库,安装react是必不可少的,而使用组件库的开发者,本地项目肯定安装了react,因此开发的组件库中不必把react打包进去(期望项目的使用者来提供这些模块的实现)。
- 版本一致性:使用你的组件库的开发者需要确保他们项目中安装了与你声明的对等依赖版本兼容的包,以确保组件库正常运行。
示例:声明要使用组件库,需在项目中安装大于17.0.1版本的react
json
"peerDependencies": {
"react": ">17.0.1"
}
不同版本npm的包依赖结构
npm2以及之前的版本
此阶段,采用的是简单的依赖树结构,嵌套安装,每个依赖项都有自己的node_modules文件夹。
导致的问题:
- 有的项目会出现过深的依赖,从而导致目录路径过长的问题
- 重复下载相同的包,比如A和C都依赖于B,会重复下载B包
Npm3
npm3使用了扁平化结构,以解决npm2中过深依赖和相同包重复下载的问题,加快了安装速度、减少冗余。
npm3中只有一个node_modules,将包放在了同一层,重复的包不再下载。
但是,如果同一个包的不同版本,就只能有一个包可以被提上来。其余版本的包还是嵌套安装在各自的依赖中,所以某种情况下,并不能解决npm2中冗余安装包的问题(npm分身)。并且这个时候,A和C所使用的,都是被提升上来的包,就只能满足一个版本的依赖,这可能会导致其他包无法工作。
而且哪个包被提升,取决于A和C的位置,这就导致依赖结构不确定。可能左边的情况下可以正常运行,下次install以后变成了右边的情况,就会出现bug。
此外,npm3还有一个问题,就是幽灵依赖。下图中,B是A和C所依赖的包,但是用户却能够直接引用,这是扁平化处理的结果,而B包就是幽灵依赖。如果我们将A和C包删除,就会导致直接引用B的地方出现问题。
npm5
一般采用packackge.json来固定依赖树的结构,团队开发过程中,将packackge.json也上传git库,大家统一使用一个结构。但是这没有解决版本冲突的问题。
yarn
yarn出现时npm正处于npm3时期,所以当时yarn和npm3一样采用了扁平化的结构,同时采用了yarn.lock来解决管理依赖和版本控制的问题,为npm5提供了借鉴。同时它提供了一些命令行工具来安装、升级和删除包。
pnpm
.pnpm
pnpm为了解决幽灵依赖的问题,在node_modules的目录下有一个名为.pnpm的文件夹,除了直接依赖的包在node_modules下,其他包都会平铺的存在于.pnpm下。
操作系统中的软连接和硬链接:
操作系统中,每个文件会有一个索引节点,该索引节点直接指向磁盘中的文件。硬链接会指向索引节点,可以直接访问到文件。
软链接(符号链接)通过创建link文件,link文件中存储着某个索引节点的绝对路径,通过访问该路径访问到索引节点从而访问文件,所以速度上会稍微慢一点(类似于超链接)。
索引节点上会计算硬链接的指向数,如果硬链接指向数为0,就会删除文件。软链接对此没有影响。
通过这两种方式可以实现文件共享,减少重复文件,节省空间。
.pnpm-store
pnpm建立了一个全局的.pnpm-store文件夹做为仓库,所有的包都被安装在该目录下。.pnpm下的依赖通过硬链接直接关联到文件,依赖与依赖之间通过软链接进行链接。
如果移除项目,想要删除全局上不再使用的包,可以执行下面的命令进行删除。
pnpm store prune
这种设计思路从根本上解决了依赖冗余、npm分身、版本冲突等问题,并且安装速度和空间使用上的效率都大大提高。
node版本管理
另外.pnpm包含了nvm的功能,可以实现node版本的管理。
总结
工具 | 特点 | 缺点 | 速度 |
---|---|---|---|
npm2 | 采用嵌套结构 | 依赖过深、冗余依赖 | |
npm3 | 采用扁平化结构和嵌套结构结合 | 版本冲突、依赖结构不稳定、幽灵依赖、npm分身 | |
npm5 | 采用package-lock.json | 版本冲突、幽灵依赖、npm分身 | |
yarn | 采用yarn.lock | 版本冲突、幽灵依赖、npm分身 | 大型项目上快于npm |
pnpm | 采用软硬链接结合以及深层存放依赖 | 最快 |