文章目录
前言
最近总有些同事问我为什么喜欢使用pnpm,除此之外慢慢开始带实习生,为了让一些基础的能够认清这些差别,所以简述下我的认知(仅供参考)
yarn、npm、cnpm 和 pnpm 是四种流行的 JavaScript 和 Node.js 包管理工具。
虽然它们都用于管理项目依赖,但在设计理念、功能和使用方式上存在一些显著的区别。
npmcnpmyarnpnpm_10">一、npm,cnpm,yarn和pnpm的基本介绍和特点
npm_Node_Package_Manager_11">1.npm (Node Package Manager)
- 基本介绍:
- 特点:
- 安装方式: 默认情况下,会将依赖包安装到每个项目的 node_modules 目录中。
- 性能: 安装速度相对较慢,尤其是当项目依赖较多时,因为每次都需要从头开始安装。
- 依赖管理: 使用扁平化依赖结构,可能导致依赖冲突(如多个版本的同一依赖)。
- 工作空间支持: 在较新版本中加入了工作空间的支持,但相对功能较为基础。
- 安装: 随着node版本安装会自带npm命令
2. Yarn
- 基本介绍:
- yarn 是 Facebook 开发的包管理工具,旨在提供更快的安装速度和更好的依赖管理。
- 特点:
npm_China_npm_30">3. cnpm (China npm)
- 基本介绍:
- 特点:
npm_38">4. pnpm
- 基本介绍:
- pnpm 是一个高效的包管理器,专注于提高安装效率和减少磁盘空间的使用。
- 特点:
npmpnpm__48">二、简述npm和pnpm 的存储方式和依赖数
1.存储方式
1. npm 的存储方式
* 依赖安装路径:默认情况下,npm 将所有依赖包安装在项目的 node_modules 目录下。每个包的依赖会在其自己的 node_modules 文件夹中。如果一个依赖包有自己的依赖(即子依赖),npm 会在该依赖包的目录下再创建一个 node_modules 文件夹来存放这些子依赖。* 扁平化依赖:在较新的版本(npm 3及之后),npm 尝试将依赖尽量扁平化。如果多个包依赖于同一版本的依赖,npm 会将其安装在顶层的node_modules 目录中。但如果不同的依赖包需要不同版本的同一依赖,仍然会在相应的依赖包目录中安装这些版本。* 空间使用:由于可能会重复安装多个版本的相同依赖,npm 的存储方式可能导致大量的磁盘空间浪费,尤其是在有多个包依赖同一库时。3. pnpm 的存储方式
* 全局存储:pnpm 使用全局存储来存放所有包。所有依赖包会被安装到一个共享的位置(通常在用户的主目录下),而项目本身的 node_modules 目录只是存放指向这些全局包的符号链接(symlinks)。这样,所有项目都可以通过符号链接访问同一依赖,从而避免重复存储。* 严格的依赖声明:pnpm 强制每个包只能直接访问其声明的依赖。这意味着包不会直接访问其父级或其他包的依赖,从而提高了模块的隔离性和可预测性。* 空间使用:由于采用了全局存储和符号链接,pnpm 在处理多个项目和依赖时,显著降低了磁盘空间的使用。这使得相同的依赖只需下载一次,无论有多少项目需要使用它。
2.依赖树
1. npm 的依赖树
当使用 npm 安装依赖时,npm 会创建一个层次化的依赖树,每个包的依赖都被单独安装在 node_modules 目录中。主要特点包括:* 扁平化依赖: npm 会尽量将依赖扁平化。在同一项目中,如果多个包依赖于同一版本的依赖,npm 会将其安装在根 node_modules 目录中。* 多版本处理: 如果不同的依赖包依赖于同一库的不同版本,npm 会在相应的包目录中安装这些版本。这就意味着,一个包可能会存在多个版本的同一依赖,每个版本位于其依赖包的 node_modules 目录中。* 依赖树结构: 依赖树可能看起来像这样:└── project├── packageA│ └── packageB│ └── packageC└── packageD└── packageB└── packageC在上述结构中,packageA 和 packageD 都依赖于 packageB,而 packageB 又依赖于 packageC。如果 packageB 需要不同版本的 packageC,则它们会被安装在各自的 node_modules 中。2. pnpm 的依赖树
pnpm 的依赖处理方式与 npm 有显著不同,主要特点包括:* 全局存储: pnpm 将所有包安装到一个全局存储位置,实际的项目中只创建符号链接(symlink)指向这个全局存储。这样可以减少磁盘空间的使用。* 严格依赖管理: pnpm 强制每个包只能访问其直接声明的依赖,这意味着包无法访问其父级或其他包的依赖,确保更加一致和可预测的依赖关系。* 依赖树结构:依赖树可能看起来像这样:├── project│ ├── node_modules│ │ ├── packageA│ │ │ └── node_modules│ │ │ └── packageB│ │ │ └── node_modules│ │ │ └── packageC│ │ └── packageD│ │ └── node_modules│ │ └── packageB│ │ └── node_modules│ │ └── packageC│ └── .pnpm└── global storage在这个结构中,packageA 和 packageD 仍然依赖于 packageB,但 pnpm 通过全局存储来确保只存储一份 packageB 和 packageC。每个包只能访问它们的直接依赖,从而避免了版本冲突的问题。
三、两者依赖树的差异导致结果的对比
请看以下内容,相信有过一两年的经验,可能使用npm或多或少都会遇到这种情况
- 使用npm i 的情况
上述其实说的问题就是依赖冲突导致不能下载依赖包
项目中postcss-loader
需要安装7.3.4,但另一个依赖要求3.0.0到4.0.0之间,这就是npm依赖树导致的问题,需要人为的手动安装指定版本,或者通过npm install --force 或者 npm install --legacy-peer-deps解决
但是需要注意的是
- –force:选项强制执行某些操作,即使遇到错误或警告,也会继续执行。使用这个选项可以忽略某些依赖冲突或不符合规范的情况。
- –legacy-peer-deps:选项使得 npm 在安装时使用旧版的对等依赖行为。具体来说,它允许不严格检查对等依赖的版本冲突。
- 使用pnpm i 的情况
它会自动处理一些依赖树的关系,也不会存在什么忽略或者强制处理,这样会减少隐藏的一些问题
四、简单说说-g,–save, --save-dev代表着什么
-g / --global:这个选项用于全局安装包。当你希望将包安装到全局环境中,以便在系统的任何地方都能使用时,通常用于命令行工具(CLI)。
–save:将安装的依赖包添加到项目的 package.json 文件的 dependencies 列表中。在 npm 5 之后,默认情况下会自动将依赖保存到 dependencies 中,因此这个选项在现代 npm 使用中不再经常需要。
-D / --save-dev:将安装的包添加到项目的 package.json 文件的 devDependencies 列表中。用于开发过程中需要的工具,例如测试框架、构建工具等,这些工具在生产环境中并不需要。
–save-exact:将安装的包的具体版本(而不是范围版本)保存到 package.json 中。当你希望确保项目在安装时使用确切的版本,而不是可能的较新版本。
–no-save:安装包时不更新 package.json 文件。临时安装某个包用于测试或调试。
–production:仅安装 dependencies 中列出的包,忽略 devDependencies。在生产环境中部署应用程序时使用。
-E / --save-exact:将依赖包的确切版本(不带波浪号或插入符号)保存到 package.json 中。
–legacy-peer-deps:忽略对等依赖的冲突,在安装时使用旧版行为。解决某些依赖冲突问题时使用。
–force:强制执行操作,忽略警告和错误,适用于需要绕过冲突或错误的情况。
总结
以上就是个人收集总结的一些小东西了,给自己总结同时记录,这也就是我为什么偏爱使用pnpm
了,因为它会无形中自动解决一些问题,希望对同道中人有点小用啦。。。