文章目录
包管理工具之-npm,cnpm,pnpm,yarn
- npm(Node Package Manager)
- 作用:
- 它是JavaScript运行时环境Node.js的默认包管理器。主要用于安装、管理和共享JavaScript代码包。例如,当你开发一个Node.js应用程序时,需要安装一些依赖包,如Express(一个流行的Web应用框架),就可以使用
npm install express
命令来安装。 - 可以管理项目的依赖关系,记录在
package.json
文件中。这个文件包含了项目所依赖的包及其版本范围。当你在一个新的环境中部署项目时,运行npm install
会根据package.json
文件自动安装所有需要的依赖包。 - 可以发布自己的包供其他开发者使用。你可以通过
npm publish
命令将自己开发的JavaScript模块发布到npm仓库中。
- 它是JavaScript运行时环境Node.js的默认包管理器。主要用于安装、管理和共享JavaScript代码包。例如,当你开发一个Node.js应用程序时,需要安装一些依赖包,如Express(一个流行的Web应用框架),就可以使用
- 特点:
- 拥有庞大的软件包生态系统,几乎涵盖了所有类型的JavaScript库和工具。
- 它是按照包的语义化版本号(SemVer)来管理依赖的。例如,一个包的版本号可能是
1.2.3
,其中1
是主版本号,2
是次版本号,3
是补丁版本号。通过在package.json
中指定版本范围(如^1.2.3
表示可以安装1.2.3
及以上的兼容版本)来控制依赖的更新。
- 作用:
- cnpm(Chinese npm)
- 作用:
- cnpm是淘宝团队做的一个npm镜像,主要目的是解决国内用户使用npm时速度慢的问题。它和npm的功能基本一致,在安装和管理JavaScript包方面的操作方式也相似。例如,
cnpm install lodash
会从淘宝镜像服务器下载lodash
包,而不是从npm官方服务器下载,这样可以加快下载速度。
- cnpm是淘宝团队做的一个npm镜像,主要目的是解决国内用户使用npm时速度慢的问题。它和npm的功能基本一致,在安装和管理JavaScript包方面的操作方式也相似。例如,
- 特点:
- 它提供了更快的下载速度,尤其对于国内网络环境下的用户来说,这是非常重要的优势。它会缓存已经下载过的包,减少了重复下载的时间。
- 不过,由于它是镜像,在某些极端情况下,可能会出现与npm官方仓库数据同步不及时的问题,但这种情况相对较少。
- 作用:
- pnpm(Performant npm)
- 作用:
- pnpm是一种快速、节省磁盘空间的包管理工具。它采用了一种非扁平化的存储方式来管理依赖包。例如,在传统的npm安装方式中,如果多个项目都依赖同一个版本的
react
包,每个项目的node_modules
目录下都会有一份react
的副本。而pnpm会将react
包存储在一个全局的存储位置,各个项目通过硬链接的方式引用,这样大大节省了磁盘空间。 - 同样可以用于安装、更新和删除项目中的依赖包。例如,
pnpm add axios
可以将axios
(一个用于发送HTTP请求的库)添加到项目中。
- pnpm是一种快速、节省磁盘空间的包管理工具。它采用了一种非扁平化的存储方式来管理依赖包。例如,在传统的npm安装方式中,如果多个项目都依赖同一个版本的
- 特点:
- 它在安装速度和磁盘空间利用方面表现出色。通过共享相同版本的依赖包,减少了磁盘空间的占用,并且由于其独特的存储结构,安装速度也有一定程度的提升。
- pnpm支持和npm类似的工作流程,在很多项目中可以作为npm的替代品使用,并且它也兼容
package.json
文件来管理依赖关系。
- 作用:
- yarn
- 作用:
- 也是一个JavaScript包管理器,和npm类似,用于安装、更新和管理项目的依赖包。例如,
yarn add vue
可以将Vue.js框架添加到项目中。它会自动解析依赖关系并下载所需的包。 - 可以生成一个
yarn.lock
文件,这个文件锁定了所有依赖包的具体版本,确保在不同环境下(开发环境、测试环境、生产环境等)安装的依赖包版本完全一致,避免了由于依赖包版本不一致而导致的潜在问题。
- 也是一个JavaScript包管理器,和npm类似,用于安装、更新和管理项目的依赖包。例如,
- 特点:
- 它的安装过程相对npm更加快速和稳定。yarn采用了并行安装的策略,能够同时下载多个依赖包,提高了安装效率。
- 它的
yarn.lock
文件提供了更严格的版本锁定机制,使得项目的依赖关系更加可预测和可控。不过,这也意味着在更新依赖包时可能需要更加谨慎,因为手动修改yarn.lock
文件可能会导致依赖关系混乱。
- 作用:
包管理工具之源头
npm
- 底层算法概述
- 依赖解析算法:npm使用嵌套的依赖树结构来管理依赖关系。当安装一个包时,它会递归地解析该包及其所有依赖项的版本,并将它们安装在项目的
node_modules
目录下。例如,安装一个有多个层级依赖的Web应用框架,它会根据package.json
中的版本要求,从npm仓库中下载对应的包版本,并按照依赖层次构建目录结构。 - 安装算法:采用简单的顺序安装方式。它会逐个处理依赖包,先安装主依赖包,然后按照依赖树的顺序依次安装其依赖的子包。这种方式在简单项目中工作良好,但在大型项目或存在复杂依赖关系的项目中,可能会导致
node_modules
目录变得庞大且混乱。
- 依赖解析算法:npm使用嵌套的依赖树结构来管理依赖关系。当安装一个包时,它会递归地解析该包及其所有依赖项的版本,并将它们安装在项目的
- 改进方向
- 为了解决依赖冲突和版本管理问题,npm引入了
package - lock.json
文件。这个文件记录了安装时每个依赖的确切版本和依赖树结构,确保在不同环境下安装的一致性。例如,当团队成员在不同的机器上安装项目依赖时,package - lock.json
可以保证大家安装的是相同版本的包。
- 为了解决依赖冲突和版本管理问题,npm引入了
- 底层算法概述
cnpm
- 底层算法概述
- 镜像同步算法:cnpm的底层算法主要在于它的镜像机制。它会定期从npm官方仓库同步包数据到自己的服务器。这个同步过程涉及到数据的获取、更新和存储。它会根据一定的策略(如定时更新、根据访问热度更新等)来确保本地镜像中的包数据与npm官方仓库保持一定程度的同步。
- 依赖解析和安装算法:在依赖解析和安装方面,基本和npm相同。它会读取
package.json
文件,解析依赖关系,然后从本地镜像服务器获取包并安装到node_modules
目录。因为是从本地镜像下载,所以下载速度会更快。
- 改进方向
- 重点在于优化镜像服务器的性能和同步效率。通过分布式存储和缓存策略,减少数据同步的时间和网络开销。同时,也在不断优化与npm官方仓库的兼容性,确保在大部分情况下可以无缝替代npm进行包的安装。
- 底层算法概述
pnpm
- 底层算法概述
- 存储算法:pnpm采用内容可寻址存储(Content - Addressed Storage,CAS)的方式来管理包。所有的包都存储在一个全局的存储区域(store)中。每个包的存储位置是由其内容(文件内容的哈希值)决定的,而不是传统的按包名和版本存储。例如,相同版本的
react
包在不同项目中只会存储一份,通过硬链接的方式在各个项目的node_modules
中引用。 - 依赖解析算法:在解析依赖关系时,pnpm会先检查全局存储中的包是否满足项目的需求。如果满足,就直接使用已有的包;如果不满足,才会从远程仓库下载新的包到全局存储中。这种方式可以有效避免重复下载相同的包,节省磁盘空间。
- 存储算法:pnpm采用内容可寻址存储(Content - Addressed Storage,CAS)的方式来管理包。所有的包都存储在一个全局的存储区域(store)中。每个包的存储位置是由其内容(文件内容的哈希值)决定的,而不是传统的按包名和版本存储。例如,相同版本的
- 改进方向
- 不断优化全局存储的管理算法,提高存储效率和包的检索速度。同时,也在改进其与现有项目的兼容性,尤其是对于那些依赖于传统
node_modules
扁平结构的工具和插件,使其能够更好地适应pnpm的非扁平存储方式。
- 不断优化全局存储的管理算法,提高存储效率和包的检索速度。同时,也在改进其与现有项目的兼容性,尤其是对于那些依赖于传统
- 底层算法概述
yarn
- 底层算法概述
- 安装算法:yarn采用并行安装算法来加速包的安装过程。它会同时处理多个依赖包的下载和安装,而不是像npm那样顺序安装。例如,在安装一个有多个依赖的大型项目时,yarn可以同时从仓库中获取多个不相关的依赖包并进行安装,大大提高了安装速度。
- 依赖锁定算法:yarn的
yarn.lock
文件是其核心算法的体现。它使用这个文件来精确锁定每个依赖包的版本。在解析依赖关系时,会首先参考yarn.lock
文件中记录的版本信息。如果package.json
中的依赖版本要求与yarn.lock
冲突,yarn会优先使用yarn.lock
中的版本,确保安装的稳定性和一致性。
- 改进方向
- 进一步优化并行安装算法,减少安装过程中的资源竞争和冲突。同时,也在不断改进
yarn.lock
文件的管理算法,例如在更新依赖包时,提供更智能的版本更新建议,以平衡项目的稳定性和对新功能的需求。
- 进一步优化并行安装算法,减少安装过程中的资源竞争和冲突。同时,也在不断改进
- 底层算法概述
依赖下载安装的完整性
确保依赖下载完整性的通用原则和方法
- 校验和验证机制
- 大多数包管理器(npm、cnpm、pnpm、yarn)在下载依赖包时,会使用校验和(如SHA - 1、SHA - 256等哈希算法)来验证下载文件的完整性。当从远程仓库下载一个包时,仓库会提供该包的哈希值,包管理器在下载完成后会计算下载文件的哈希值,并与仓库提供的哈希值进行比较。如果两者不匹配,就说明文件可能在传输过程中损坏,包管理器会重新下载或者提示错误。
- 重试机制
- 这些包管理器通常都有重试机制。如果在下载过程中出现网络问题(如连接中断、超时等),包管理器会尝试重新连接并继续下载。例如,npm会在一定次数内(通常有默认的重试次数设置)自动重试下载,直到成功或者达到重试次数上限。
- 依赖锁定文件
- npm的
package - lock.json
和yarn的yarn.lock
:这两个文件记录了依赖包的精确版本和文件树结构。在安装依赖时,包管理器会严格按照锁定文件中的信息进行安装。以package - lock.json
为例,它包含了每个依赖包的名称、版本、下载地址、以及依赖关系树等详细信息。当重新安装依赖时,npm会根据这些信息确保下载的是相同版本和相同内容的包,从而保证了完整性。 - pnpm的存储机制:pnpm通过内容可寻址存储(CAS),每个包在存储区域(store)中的位置是由其内容的哈希值决定的。在安装依赖时,它会先检查全局存储中的包是否满足项目需求,如果满足就直接使用,这种方式可以确保每次使用的包内容都是完整且一致的。
- npm的
- 校验和验证机制
关于
cnpm i --force
的原理与应用- 原理
--force
是一个强制安装选项。正常情况下,cnpm会根据package.json
中的版本范围和本地缓存(如果有)来决定是否更新或安装依赖包。当使用--force
选项时,它会忽略本地缓存和已有的版本信息,强制从远程镜像(对于cnpm来说,是其淘宝镜像服务器)重新下载并安装所有依赖包。在这个过程中,它依然会使用校验和机制来验证下载文件的完整性。
- 应用场景
- 当你怀疑本地缓存的包损坏或者版本不一致导致项目出现问题时,可以使用
--force
选项。例如,在开发过程中,如果出现一些奇怪的依赖相关的错误,可能是因为之前的安装过程被中断,导致部分文件损坏,此时使用--force
可以重新下载完整的、最新的依赖包来解决问题。不过,需要注意的是,使用--force
可能会导致一些意想不到的问题,比如破坏了package - lock.json
(对于npm项目)或者yarn.lock
(对于yarn项目)文件中锁定的版本一致性,所以应该谨慎使用。
- 当你怀疑本地缓存的包损坏或者版本不一致导致项目出现问题时,可以使用
- 原理
node 与 npm 之间的关系
Node.js是运行环境,npm是其生态系统中的包管理器
- Node.js是一个基于Chrome V8引擎的JavaScript运行时环境,它使得JavaScript可以在服务器端运行。这意味着可以使用JavaScript编写服务器端应用程序、命令行工具等。例如,通过Node.js可以构建一个简单的Web服务器来处理HTTP请求。
- npm(Node Package Manager)是随同Node.js一起安装的包管理器,它主要用于管理Node.js项目中的依赖包。在Node.js生态系统中,有大量的第三方库和工具,如Express(用于构建Web应用程序的框架)、Mocha(用于单元测试的工具)等,这些都可以通过npm进行安装和管理。
npm依赖于Node.js运行
- npm本身是用JavaScript编写的命令行工具,它运行在Node.js环境中。当在终端中输入
npm
命令(如npm install
、npm start
等)时,Node.js会解析并执行这些命令。例如,npm install
命令会根据package.json
文件中的配置信息,在Node.js的帮助下,从npm仓库中下载并安装相应的依赖包到项目的node_modules
目录中。
- npm本身是用JavaScript编写的命令行工具,它运行在Node.js环境中。当在终端中输入
Node.js项目通常借助npm来管理依赖和构建生态系统
- 在开发一个Node.js应用程序时,几乎都会用到npm来添加所需的功能模块。例如,要构建一个RESTful API服务器,可能需要安装
express
框架和body - parser
中间件(用于解析请求体)。可以通过npm install express body - parser
命令来添加这些依赖。 - 同时,
package.json
文件是Node.js项目和npm之间的重要纽带。这个文件记录了项目的基本信息(如名称、版本、作者等)和依赖信息(包括生产环境和开发环境的依赖)。npm会根据package.json
文件中的依赖配置来安装、更新和管理项目中的依赖包。例如,package.json
文件中的"dependencies"
字段列出了项目运行时所需要的包,"devDependencies"
字段列出了开发过程中需要的工具(如测试框架、代码格式化工具等)。
- 在开发一个Node.js应用程序时,几乎都会用到npm来添加所需的功能模块。例如,要构建一个RESTful API服务器,可能需要安装
如何手动管理依赖
了解依赖的类型
- 在Node.js项目中,依赖主要分为两种类型:生产依赖(
dependencies
)和开发依赖(devDependencies
)。 - 生产依赖:是项目在生产环境(实际运行环境)中运行所必需的依赖。例如,对于一个Web应用程序,
express
(一个用于构建Web服务器的框架)就是生产依赖,因为没有它,应用程序无法正常运行并提供服务。 - 开发依赖:是在开发过程中使用的依赖,如测试框架(
mocha
、jest
)、代码格式化工具(prettier
)等。这些依赖在项目实际运行时通常不需要,仅用于开发阶段来提高代码质量和测试效率。
- 在Node.js项目中,依赖主要分为两种类型:生产依赖(
不使用包管理器,手动下载依赖包
- 步骤一:确定依赖包的来源
- 可以从官方网站(如果有)或可靠的代码仓库(如GitHub)获取依赖包的源代码。以
express
为例,可以访问其官方网站或GitHub仓库,找到适合项目的版本并下载。
- 可以从官方网站(如果有)或可靠的代码仓库(如GitHub)获取依赖包的源代码。以
- 步骤二:解压和放置依赖包
- 将下载的依赖包解压到项目的
node_modules
目录下。如果没有node_modules
目录,可以手动创建一个。每个依赖包通常是一个包含package.json
、index.js
等文件的文件夹结构。例如,将express
包解压后的文件夹放置在node_modules
目录下,就可以在项目代码中引用express
模块了。
- 将下载的依赖包解压到项目的
- 步骤三:管理依赖版本和更新
- 这种手动方式很难管理依赖的版本。要更新依赖,需要重复上述步骤,手动查找和下载最新版本,并且需要注意新的版本是否与项目中的其他依赖兼容。而且没有包管理器的自动版本检查和冲突解决机制,容易出现依赖冲突问题。
- 步骤一:确定依赖包的来源
通过修改
package.json
文件手动管理依赖(以npm为例)- 步骤一:创建或编辑
package.json
文件- 如果项目没有
package.json
文件,可以通过在项目根目录下运行npm init
命令来创建一个。这个文件记录了项目的基本信息和依赖信息。在package.json
文件中,有两个主要的字段用于管理依赖:"dependencies"
和"devDependencies"
。
- 如果项目没有
- 步骤二:添加生产依赖
- 例如,要添加
express
作为生产依赖,可以在package.json
文件的"dependencies"
字段中添加"express": "^4.18.2"
(这里^4.18.2
是版本号范围,表示可以安装4.18.2
及以上的兼容版本)。然后在项目根目录下运行npm install
命令,npm会根据package.json
中的信息下载express
包到node_modules
目录中。
- 例如,要添加
- 步骤三:添加开发依赖
- 对于开发依赖,如
mocha
(一个测试框架),可以在package.json
文件的"devDependencies"
字段中添加"mocha": "^10.2.0"
,然后运行npm install --save - dev
命令来安装mocha
。这个命令会将mocha
安装到node_modules
目录中,并将其添加到package.json
的"devDependencies"
字段中。
- 对于开发依赖,如
- 步骤四:更新和删除依赖
- 要更新依赖,可以修改
package.json
文件中的版本号范围,然后运行npm update
命令。例如,将express
的版本号范围从^4.18.2
修改为^5.0.0
,然后运行npm update
,npm会尝试更新express
到符合新范围的版本。要删除依赖,可以直接从package.json
文件的相应字段(dependencies
或devDependencies
)中删除该依赖的条目,然后运行npm uninstall
命令,npm会从node_modules
目录中删除对应的依赖包。
- 要更新依赖,可以修改
- 步骤一:创建或编辑
手动管理依赖的优优缺点
优点
- 深度理解依赖关系
- 手动管理依赖时,开发者需要亲自下载、解压和配置每个依赖包。这使得开发者能够深入了解每个依赖在项目中的作用、其内部结构以及与其他依赖的相互关系。例如,在手动将
express
框架添加到项目中时,开发者会直接接触到express
的文件结构,了解其核心模块、中间件的存储位置等,这有助于更好地理解整个项目的架构。
- 手动管理依赖时,开发者需要亲自下载、解压和配置每个依赖包。这使得开发者能够深入了解每个依赖在项目中的作用、其内部结构以及与其他依赖的相互关系。例如,在手动将
- 高度定制化
- 可以根据项目的特殊需求进行定制化操作。如果项目对某个依赖有特定的修改要求,手动管理可以方便地对依赖包进行局部调整。比如,对于一个安全性要求较高的项目,开发者可以手动审查和修改依赖包中的某些安全相关代码,而这在使用自动化包管理器时可能会比较复杂。
- 不依赖外部工具(在一定程度上)
- 在某些环境下,可能无法使用包管理器(如网络限制、安全策略等)。手动管理依赖可以让项目在不依赖外部包管理器的情况下,依然能够添加和更新所需的依赖。例如,在一个封闭的开发环境中,通过手动从本地存储设备获取依赖包并添加到项目中,使项目能够继续推进。
- 深度理解依赖关系
缺点
- 效率低下
- 手动下载和配置依赖包是一个繁琐且耗时的过程。尤其是当项目依赖众多时,需要逐个查找、下载、解压和放置依赖包,这会花费大量的时间和精力。例如,一个复杂的Node.js项目可能有几十个甚至上百个依赖,手动管理这些依赖会使项目的初始化和更新过程变得非常缓慢。
- 容易出现版本冲突
- 手动管理很难有效地控制依赖版本。没有像包管理器那样的自动版本检查和冲突解决机制,开发者需要自己跟踪每个依赖的版本兼容性。例如,当更新一个依赖时,可能会因为与其他依赖的版本不兼容而导致项目出现错误,而且很难快速定位和解决这些冲突。
- 维护成本高
- 随着项目的发展和依赖的更新,手动管理依赖会变得越来越困难。每次更新依赖都需要手动重复下载、解压等操作,并且要确保新的版本不会破坏项目的现有功能。在团队开发环境中,这种方式还会导致不同开发者的依赖环境不一致,增加了项目的维护成本和沟通成本。
- 效率低下
镜像源安装及下载
cnpm
npm install -g cnpm --registry=http://registry.npmmirror.com
设置镜像源:
npm config set registry http://registry.npmmirror.com
恢复镜像源:
npm config set registry https://registry.npmjs.org/
pnpm(add(new),remove,install(配置文件下载))
npm install pnpm -g
设置镜像源
pnpm config set registry https://registry.npmmirror.com/
恢复镜像源:
npm config set registry https://registry.npmjs.org/
yarn(add(new),remove,install(配置文件下载))
npm i yarn -g
设置镜像源:
yarn config set registry https://registry.npmmirror.com
恢复镜像源:
yarn config set registry https://registry.yarnpkg.com