Nodejs 知识体系(二): NPMNPM 的概念 NPM(Node Package Manager)是 Node.j
NPM 的概念
NPM(Node Package Manager)是 Node.js 的官方包管理器,用于管理、发布和安装 JavaScript 代码的模块和包。它允许开发者轻松安装、更新和管理项目的依赖库,同时提供了一个丰富的公共注册表,让开发者可以共享和重用代码。通过 NPM,开发者能够快速构建应用程序,维护代码的版本一致性,并简化项目的依赖管理过程。
简单来说, Node.js 提供了执行环境,而 NPM 负责包的管理和依赖解决,两者共同支持高效的 JavaScript 开发。
NPM(Node Package Manager)为开发者提供了一个灵活的包管理系统,允许任何开发者在不重名的情况下发布自己的 Node.js 包,促进了模块的共享与重用。NPM 通过 package.json
文件来管理项目的依赖、缓存、版本和代码,使得依赖管理变得高效而透明。与 Ruby 的 Gem、Java 的 Maven 和 Python 的 Pip 类似,NPM 提供了一种中心化的管理方式,但与 Maven 的磁盘路径引用不同,NPM 强调了基于网络的分发和版本控制。这种设计使得 Node.js 的生态系统更加开放和动态,为开发者提供了丰富的工具和库来加速开发过程。
为什么 node 要不断的升级版本?
Node.js 不断升级版本是为了提高安全性、性能和功能。每个新版本修复安全漏洞、优化性能、引入最新的 JavaScript 特性,并修复已知的 bug。这些升级确保了 Node.js 兼容现代标准,并在不断变化的技术环境中保持稳定和可靠的运行时环境。
如何管理和切换 node 版本
NPM 的目标
NPM(Node Package Manager)的主要目标是简化 JavaScript 项目的依赖管理和模块共享。
简化依赖管理: 提供一个工具来轻松安装、更新和管理项目中的第三方依赖包,减少手动管理依赖的复杂性。
模块共享和重用: 允许开发者将自己的代码作为模块发布到公共或私有的 NPM 注册表,使其他开发者能够重用这些模块,促进代码的共享和协作。
构建生态系统: 通过 NPM 注册表,建立一个庞大的开源社区和生态系统,使得各种 JavaScript 工具、库和框架可以被快速发现和使用。
自动化工作流: 提供脚本功能,允许开发者定义和运行自动化任务(如测试、构建和部署),简化开发流程。
支持版本控制: 允许对依赖项进行版本控制和管理,确保项目在不同环境中能够稳定运行,并能够方便地回退到先前的版本。
NPM 项目的创建
npm init
package.json
package.json 是 Node.js 项目的核心配置文件,用于定义项目的元数据、依赖关系、脚本命令和其他配置信息。它记录了项目的基本信息(如名称、版本、描述)、依赖的包及其版本、开发工具和构建脚本等。通过 package.json,开发者可以管理项目的依赖版本、运行项目相关的脚本任务,并确保项目在不同环境中具有一致的配置和行为。
{
"name": "colion.dict.view",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
package.json 常见字段详解
name: 包的名称 。
如果发布的是命令行工具,且 bin 字段未指定命令名称,则默认使用包名作为命令名称。
可以是一个普通的名称,如 my-cli-tool,也可以是带有作用域的名称,如 @myorg/my-cli-tool。
version: 包的版本号。
版本规范
SemVer(语义版本控制) : 格式为 major.minor.patch。
- ^4.x.y 允许更新 minor 和 patch 版本。
- ~4.x.y 允许更新 patch 版本。
- 4.x.y 固定版本,不允许更新。
版本更新原则:
- major 版本更新:引入不兼容的 API 改动。
- minor 版本更新:新增功能,向后兼容。
- patch 版本更新:修复 bug,向后兼容。
npm ci 和 npm install 有什么区别?
- 安装方式
npm install: 会安装 package.json 中列出的依赖。如果 package-lock.json 存在,它会尽量匹配 package-lock.json 中的版本,但会根据 package.json 的要求来更新 package-lock.json。
npm ci: 会根据 package-lock.json 完全一致地安装依赖,不会更新 package-lock.json。如果 package-lock.json 和 package.json 不一致,npm ci 会失败。
- 性能
npm install: 适合日常开发和添加新依赖时,执行较慢,特别是在 package-lock.json 和 package.json 不一致时,可能会进行大量的计算和版本解析。
npm ci: 主要用于持续集成环境,执行更快,因为它不需要进行版本解析,而是直接使用 package-lock.json 安装指定版本的依赖。
- 用途
npm install: 用于本地开发和更新依赖。
npm ci: 适用于 CI/CD 环境或在生产环境中需要确保依赖一致性时使用。
- 处理 node_modules:
npm install: 会保留 node_modules 中已有的依赖,并更新它们。
npm ci: 会删除整个 node_modules 文件夹,并重新安装所有依赖,确保环境干净一致。
main: 包的入口文件。
- 指定了包的主文件,其他程序引用此包时将使用此文件。
- 常用于 CommonJS 规范。
bin: 指定可执行的命令。
- 如果 bin 字段定义了命令,则命令名称为 bin 字段中定义的名称。
- bin: "lib/index.js": 命令名称为包名。
- bin: {"@scoped/name": "lib/index.js"}: 命令名称为 @scoped/name。
scripts: 自定义脚本命令。
- 允许定义在开发过程中常用的命令,如 start, test, build 等。
- 使用方式:npm run [script-name]。
- 本地安装的工具将会在 node_modules/.bin 目录下被找到执行,而不是全局安装的工具。
dependencies: 产环境依赖。
- 这些依赖是项目在生产环境中运行所需的,通常包括项目代码的组成部分。
devDependencies: 开发环境依赖。
- 这些依赖仅在开发过程中使用,如测试框架、构建工具(例如 ESLint、Webpack),在生产环境中不会用到。
peerDependencies: 同版本依赖。
- 这种依赖要求宿主环境(例如使用该包的项目)中必须已安装相应的依赖包,但不会自动安装这些依赖。
- 例如,React 组件库通常不会直接依赖 React,而是通过 peerDependencies 要求宿主环境中已有 React。
optionalDependencies: 可选依赖。
- 这些依赖是可选的,安装时不会失败,即使这些依赖安装失败,项目也能正常运行。
- 适用于一些增强功能或非核心功能的依赖。
bundledDependencies : 联邦依赖。
- 用于定义项目打包时要包括的依赖项。这些依赖项会被包含在发布的包中,从而在用户安装你的包时,它们也会被自动安装。这个功能确保了即使用户的环境中没有这些依赖项,你的包仍能正常工作。
package.json 是管理 Node.js 项目的关键配置文件,定义了项目的基本信息、依赖关系、脚本命令等。合理配置 package.json 可以提高开发效率,并确保项目的依赖关系和构建流程的稳定性。
如何在本地修改第三方依赖?
- 使用 npm link
npm link 允许你在本地开发过程中将一个本地包链接到你的项目中,从而可以在开发时对其进行实时测试和调试。以下是详细的步骤和使用方式
# 在第三方包的目录中, 运行 npm link 创建全局链接
npm link
# 在主项目中, 在主项目中使用本地链接的包
npm link third-party-package
# 检查本地链接,确认该链接指向了全局的本地包
ls -l node_modules/your-local-package
# 移除链接
npm unlink your-local-package
修改后,重新构建或重启应用以看到更改效果。
- 直接修改 node_modules
- 修改本地的 node_modules/third-party-package 文件夹中的代码。
- 注意:这种方式不推荐,因为每次安装或更新依赖时,修改会被覆盖。
NPM 、Yarn 和 Pnpm 的区别?
在安装速度上:
NPM: 相对较慢,尤其是在处理大量依赖时。每次安装依赖时都会重新下载和解压包。
Yarn: 提供更快的安装速度,通过缓存机制和并行下载来提高效率。
PNPM: 最快的安装速度之一,使用硬链接和全局缓存机制,避免重复下载和存储,提高了效率。
磁盘空间
NPM: 每个项目都单独存储依赖,可能导致磁盘空间的浪费。
Yarn: 使用缓存机制,减少重复下载,但每个项目仍然单独存储依赖。
PNPM: 最有效地节省磁盘空间,通过硬链接和全局缓存来共享依赖,避免重复存储。
- 全局存储:所有项目共享一个全局存储的依赖包,每个项目的 node_modules 目录只包含指向全局存储的硬链接,而不是实际的包文件。这种方式减少了重复下载和存储相同的依赖。
- 严格的依赖结构:通过将依赖包放在一个平坦的全局目录中,PNPM 确保了依赖的严格一致性和透明性。
依赖管理
NPM: 从 NPM 5.x 开始引入 package-lock.json 文件来锁定依赖版本,但之前的版本在一致性上可能存在问题。
Yarn: 使用 yarn.lock 文件来确保一致的依赖版本,改进了依赖项的一致性管理。
PNPM: 使用 pnpm-lock.yaml 文件,同时采用严格的依赖管理,避免了潜在的版本冲突和不一致。
工作区支持
NPM: 从 7.x 开始引入工作区支持,但相较于 Yarn 和 PNPM,功能较为基础。
Yarn: 提供强大的工作区功能,适合管理多个包和项目中的依赖。
PNPM: 也支持工作区功能,管理多个包和项目中的依赖,并且优化了依赖的安装和共享。
离线模式
NPM: 不支持离线模式,依赖必须每次从网络中下载。
Yarn: 支持离线模式,一旦依赖下载到缓存中,可以在离线状态下安装。
PNPM: 支持离线模式,通过全局缓存机制,离线时也可以快速安装已缓存的依赖。
配置文件
NPM: 使用 package-lock.json 文件来锁定依赖版本。
Yarn: 使用 yarn.lock 文件来锁定依赖版本。
PNPM: 使用 pnpm-lock.yaml 文件来锁定依赖版本。
安全性
NPM: 有时会遇到安全问题,需定期更新和检查依赖,使用工具如 npm audit 来扫描漏洞。
Yarn: 也支持安全扫描,并且在某些版本中加强了安全性。
PNPM: 提供安全性扫描,确保安装的包符合安全标准。
特性 | NPM | Yarn | PNPM |
---|---|---|---|
安装速度 | 较慢 | 快 | 非常快 |
磁盘空间 | 可能浪费空间 | 有缓存但仍单独存储 | 节省空间,通过硬链接和全局缓存 |
依赖管理 | package-lock.json | yarn.lock | pnpm-lock.yaml |
工作区支持 | 从 7.x 开始 | 强大的工作区功能 | 支持工作区功能,优化安装和共享 |
离线模式 | 不支持 | 支持 | 支持 |
安全性 | npm audit | 安全扫描支持 | 安全性扫描,符合安全标准 |
PNPM 最关键的一个, 解决幽灵依赖的问题
幽灵依赖(phantom dependencies)指的是那些在项目的 package.json 文件中未显式声明但在运行时被意外使用的依赖。由于这些依赖没有在项目的依赖列表中明确列出,它们的存在可能导致以下问题:依赖管理的不一致、难以重现的错误、版本冲突和难以调试的问题。幽灵依赖通常是由错误的包管理、间接依赖或开发环境配置不当造成的。有效的包管理工具如 PNPM 能通过严格的依赖声明和全局缓存机制来减少或消除这些问题。
假设你有一个项目,package.json 只包含对 express 的依赖:
{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1"
}
}
在你的代码中,你使用了 express 的一个插件 express-session,这个插件内部使用了 lodash。但你没有在 package.json 中显式声明 lodash:
// 使用 express-session,内部依赖 lodash
const session = require('express-session');
幽灵依赖的表现:
问题: 当你运行 npm install 时,lodash 可能没有被安装,因为你没有在 package.json 中声明它。
结果: 如果你在其他环境或机器上运行项目,可能会遇到错误,因为 lodash 没有被正确安装。
PNPM 的解决方案:
PNPM 会检查 package.json 中的所有依赖,并确保所有间接依赖也被正确声明。如果 lodash 是 express-session 的隐式依赖,PNPM 会提醒你在 package.json 中添加它,避免缺失依赖的问题。
自动安装 Peer Dependencies:NPM v7 开始自动安装 peerDependencies,这意味着在安装主依赖时,NPM 会自动安装所有必要的同行依赖,避免了因缺少 peerDependencies 导致的运行时错误。
更严格的依赖树管理:NPM v7 改进了依赖树的处理,更准确地管理并安装所有依赖,避免了由于依赖不一致或缺失导致的问题。
npx 是什么?
npx 是 Node.js 生态系统中的一个命令行工具,随 npm(从版本 5.2.0 开始)一起发布。npx 的主要功能是执行 Node.js 包中的二进制文件,特别是那些不需要全局安装的工具。它使得运行这些工具变得更加便捷,无需事先安装这些工具作为全局依赖。
如何使用 npx 执行包中的二进制文件?
- 确保包已经安装:
本地依赖:npx 会首先检查你的项目的 node_modules 目录,看是否已经安装了该包的依赖。如果已安装,它会直接使用这些依赖。
全局依赖:如果包没有在本地安装,npx 会从 npm 注册表中下载并执行包的二进制文件。
- 使用 npx 运行命令:
过 npx 执行包中的二进制文件的命令,npx 会自动查找并运行该工具的可执行文件。
# 直接运行命令
npx create-react-app my-app
# 运行本地安装的工具:
npx webpack --config webpack.config.js
# 运行特定版本的工具
npx typescript@4.0.0 --version
# 从 GitHub 执行脚本
npx degit user/repo my-project
# 假设你有一个包,其中包含多个命令,但你只想运行其中的一个特定命令。你可以使用 --exec 来指定要运行的命令。
npx --exec <command> [arguments]
npx --exec my-tool my-command
如何处理 npm 中的版本冲突?
在使用 npm 时,版本冲突通常指多个依赖包之间需要的版本不一致,导致同一个包的不同版本可能被安装。这种冲突可能影响应用的稳定性和兼容性。处理 npm 版本冲突的最佳策略取决于具体项目的依赖关系和兼容性需求。通过检查依赖、升级版本、手动调整以及利用工具,可以有效解决版本冲突问题。
下面是处理 npm 版本冲突的几种方式:
使用 resolutions 字段
在 package.json 中,resolutions 字段可以强制所有依赖包使用特定版本,通常在 monorepo 或 Yarn workspace 中常用:
{
"resolutions": {
"lodash": "4.17.21"
}
}
这种方式可以指定项目中依赖的包版本,强制所有子依赖使用同一个版本。
升级依赖
通过检查 package.json 中的依赖项,并将它们升级到最新的兼容版本以解决冲突。可以使用 npm outdated 查看项目中需要更新的包:
npm outdated
npm update <package-name>
使用 npx npm-check-updates
借助 npm-check-updates 工具,可以自动检查并更新项目中的依赖到最新版本:
npx npm-check-updates -u
npm install
手动解决冲突
在某些情况下,你需要手动查看和调整 package.json 文件中的版本号。可以查看具体的冲突情况,明确是否可以升级依赖,或者是否需要锁定到某个版本。
peerDependencies
如果某个包有对其他包版本的严格要求,可以通过 peerDependencies 来解决版本冲突。在 package.json 中明确列出某个依赖的兼容版本范围:
{
"peerDependencies": {
"react": ">=17.0.0"
}
}
安装工具
工具如 npm dedupe 可以减少依赖树中的重复包:
npm dedupe
这个命令会尽可能合并重复的依赖版本,减少冲突。
使用 lock 文件
确保 package-lock.json 是最新的,并尽量使用 npm ci 来安装依赖,保持依赖的一致性,避免版本不一致引发的冲突。
转载自:https://juejin.cn/post/7410703833326518291