包管理工具的变迁(浅析)npm、yarn与pnpm

npm

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 项目代码仓库地址

对等依赖的作用:

  1. 减小打包体积:例如使用react开发的组件库,安装react是必不可少的,而使用组件库的开发者,本地项目肯定安装了react,因此开发的组件库中不必把react打包进去(期望项目的使用者来提供这些模块的实现)。
  2. 版本一致性:使用你的组件库的开发者需要确保他们项目中安装了与你声明的对等依赖版本兼容的包,以确保组件库正常运行。

示例:声明要使用组件库,需在项目中安装大于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官方网站

yarn出现时npm正处于npm3时期,所以当时yarn和npm3一样采用了扁平化的结构,同时采用了yarn.lock来解决管理依赖和版本控制的问题,为npm5提供了借鉴。同时它提供了一些命令行工具来安装、升级和删除包。

pnpm

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 采用软硬链接结合以及深层存放依赖 最快
相关推荐
熊的猫3 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
Missmiaomiao3 小时前
npm install慢
前端·npm·node.js
理想不理想v13 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
fg_41116 小时前
无网络安装ionic和运行
前端·npm
暮毅17 小时前
10.Node.js连接MongoDb
数据库·mongodb·node.js
~甲壳虫1 天前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
~甲壳虫1 天前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫1 天前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
熊的猫1 天前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
前端青山1 天前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js