npm - Node.js包管理器

1. 什么是npm?

npm(Node Package Manager)是Node.js的默认包管理器,也是世界上最大的开源软件注册表。它允许开发者共享和复用代码,管理项目依赖,并发布自己的包。npm已经成为JavaScript生态系统中不可或缺的工具,几乎所有Node.js项目都会使用npm来管理依赖。

1.1 npm的组成部分

  • npm注册表:一个公共的包数据库,存储着数百万个开源包
  • npm命令行工具:用于安装、发布、管理包的命令行工具
  • npm网站:用于浏览包、管理用户账户的网站

1.2 npm的作用

  • 安装依赖:从注册表下载并安装包
  • 管理版本:控制包的版本,避免版本冲突
  • 脚本运行:定义和运行项目脚本
  • 发布包:将自己的代码发布为npm包
  • 管理依赖树:处理包之间的依赖关系

2. 安装与配置

2.1 安装npm

npm通常随Node.js一起安装,当你安装Node.js时,npm也会被自动安装。

检查npm版本

npm -v
# 或
npm --version

更新npm

# 更新到最新版本
npm install -g npm

# 更新到特定版本
npm install -g npm@6.14.15

2.2 配置npm

查看配置

# 查看所有配置
npm config list

# 查看特定配置
npm config get registry
npm config get prefix
npm config get cache

设置配置

# 设置registry
npm config set registry https://registry.npmjs.org/

# 设置前缀(全局安装路径)
npm config set prefix "C:\\Users\\Username\\AppData\\Roaming\\npm"  # Windows
npm config set prefix "~/.npm-global"  # macOS/Linux

# 设置缓存目录
npm config set cache "C:\\Users\\Username\\AppData\\Roaming\\npm-cache"  # Windows
npm config set cache "~/.npm"  # macOS/Linux

# 设置代理
npm config set proxy http://proxy.company.com:8080
npm config set https-proxy http://proxy.company.com:8080

# 取消代理
npm config delete proxy
npm config delete https-proxy

使用配置文件

npm的配置也可以通过.npmrc文件来设置,这个文件可以存在于以下位置:

  1. 项目级./.npmrc - 仅适用于当前项目
  2. 用户级~/.npmrc - 适用于当前用户的所有项目
  3. 全局级$npm_config_prefix/etc/npmrc - 适用于系统所有用户
# .npmrc示例
registry=https://registry.npmjs.org/
prefix=~/.npm-global
cache=~/.npm

# 身份验证信息
//registry.npmjs.org/:_authToken=your_auth_token

3. 基本使用

3.1 初始化项目

# 交互式初始化
npm init

# 快速初始化(使用默认值)
npm init -y
npm init --yes

# 初始化并指定参数
npm init --scope=@username
npm init --private

初始化后会生成package.json文件,包含项目的基本信息和依赖配置。

3.2 安装依赖

本地安装

本地安装的包会被安装到项目的node_modules目录中,仅在当前项目中可用。

# 安装单个包
npm install lodash
npm i lodash

# 安装指定版本
npm install lodash@4.17.21

# 安装多个包
npm install lodash express mongoose

# 安装并保存到 dependencies
npm install lodash --save
npm i lodash -S

# 安装并保存到 devDependencies
npm install lodash --save-dev
npm i lodash -D

# 安装并保存到 peerDependencies
npm install lodash --save-peer
npm i lodash -P

# 安装并保存到 optionalDependencies
npm install lodash --save-optional
npm i lodash -O

全局安装

全局安装的包会被安装到npm的全局目录中,在任何项目中都可用。

# 全局安装
npm install -g nodemon
npm i -g nodemon

# 全局安装指定版本
npm install -g nodemon@2.0.15

安装所有依赖

# 安装package.json中定义的所有依赖
npm install
npm i

# 只安装dependencies
npm install --production

# 跳过可选依赖
npm install --no-optional

# 强制重新安装
npm install --force

3.3 查看依赖

# 查看已安装的包
npm list
npm ls

# 查看顶层依赖
npm list --depth=0

# 查看全局安装的包
npm list -g
npm list -g --depth=0

# 查看包的详细信息
npm view lodash
npm info lodash

# 查看包的版本
npm view lodash version

# 查看包的所有版本
npm view lodash versions

# 查看包的依赖
npm view lodash dependencies

3.4 更新依赖

# 检查可更新的包
npm outdated

# 更新单个包
npm update lodash
npm up lodash

# 更新所有包
npm update
npm up

# 更新到最新版本
npm install lodash@latest

# 使用npm-check-updates更新到最新版本
npm install -g npm-check-updates
ncu -u
npm install

3.5 卸载依赖

# 卸载本地包
npm uninstall lodash
npm un lodash
npm remove lodash
npm r lodash

# 卸载并从package.json中移除
npm uninstall --save lodash
npm un -S lodash

# 卸载开发依赖
npm uninstall --save-dev lodash
npm un -D lodash

# 卸载全局包
npm uninstall -g nodemon
npm un -g nodemon

4. package.json文件

package.json是npm项目的核心配置文件,包含了项目的基本信息、依赖、脚本等。

4.1 package.json结构

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "我的项目描述",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest",
    "build": "webpack"
  },
  "keywords": ["nodejs", "express", "mongodb"],
  "author": "Your Name <your.email@example.com>",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1",
    "mongoose": "^6.0.12"
  },
  "devDependencies": {
    "nodemon": "^2.0.15",
    "jest": "^27.3.1",
    "webpack": "^5.64.4"
  },
  "peerDependencies": {
    "react": "^17.0.2"
  },
  "optionalDependencies": {
    "fsevents": "^2.3.2"
  },
  "engines": {
    "node": ">=14.0.0",
    "npm": ">=6.0.0"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/username/my-project.git"
  },
  "bugs": {
    "url": "https://github.com/username/my-project/issues"
  },
  "homepage": "https://github.com/username/my-project#readme"
}

4.2 字段说明

  • name:项目名称,必须唯一,用于发布到npm
  • version:项目版本,遵循语义化版本规范
  • description:项目描述,用于npm搜索
  • main:主入口文件
  • scripts:定义项目脚本
  • keywords:关键词,用于npm搜索
  • author:作者信息
  • license:许可证
  • dependencies:生产环境依赖
  • devDependencies:开发环境依赖
  • peerDependencies:对等依赖
  • optionalDependencies:可选依赖
  • engines:指定Node.js和npm版本要求
  • repository:代码仓库信息
  • bugs:问题跟踪地址
  • homepage:项目主页

4.3 版本号规则

npm使用语义化版本(SemVer)规范,版本号格式为:主版本.次版本.补丁版本

  • 主版本:不兼容的API变更
  • 次版本:向后兼容的功能添加
  • 补丁版本:向后兼容的bug修复

版本范围

{
  "dependencies": {
    "package": "1.2.3",      // 精确版本
    "package": "^1.2.3",     // 兼容版本(>=1.2.3 <2.0.0)
    "package": "~1.2.3",     // 补丁版本(>=1.2.3 <1.3.0)
    "package": ">1.2.3",     // 大于指定版本
    "package": ">=1.2.3",    // 大于等于指定版本
    "package": "<1.2.3",     // 小于指定版本
    "package": "<=1.2.3",    // 小于等于指定版本
    "package": "1.2.x",      // 任意1.2.x版本
    "package": "*",          // 任意版本
    "package": "latest"      // 最新版本
  }
}

5. 脚本管理

npm允许在package.json中定义脚本,方便执行各种任务。

5.1 运行脚本

# 运行脚本
npm run script-name

# 运行简写脚本(start, test, restart, stop)
npm start
npm test
npm restart
npm stop

# 传递参数给脚本
npm run build -- --watch

# 运行多个脚本
npm run script1 && npm run script2

5.2 预脚本和后脚本

npm支持预脚本(pre)和后脚本(post),当运行一个脚本时,会自动先运行对应的pre脚本,后运行对应的post脚本。

{
  "scripts": {
    "prebuild": "echo 开始构建...",
    "build": "webpack",
    "postbuild": "echo 构建完成!",
    "pretest": "echo 开始测试...",
    "test": "jest",
    "posttest": "echo 测试完成!"
  }
}

当运行npm run build时,执行顺序为:

  1. npm run prebuild
  2. npm run build
  3. npm run postbuild

5.3 环境变量

在npm脚本中,可以使用环境变量,包括:

  • npm内置变量npm_package_name, npm_package_version
  • 自定义变量:在运行脚本时设置
{
  "scripts": {
    "start": "echo 项目名称: $npm_package_name, 版本: $npm_package_version",
    "dev": "NODE_ENV=development node index.js",
    "prod": "NODE_ENV=production node index.js"
  }
}

使用cross-env

在Windows和Unix系统中,设置环境变量的语法不同,使用cross-env可以统一语法。

npm install --save-dev cross-env
{
  "scripts": {
    "dev": "cross-env NODE_ENV=development node index.js",
    "prod": "cross-env NODE_ENV=production node index.js"
  }
}

5.4 常用脚本示例

{
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "build": "webpack",
    "build:watch": "webpack --watch",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier --write .",
    "deploy": "npm run build && npm run test",
    "clean": "rimraf dist node_modules",
    "reinstall": "npm run clean && npm install",
    "upgrade": "ncu -u && npm install"
  }
}

6. 依赖管理

6.1 依赖类型

  • dependencies:生产环境依赖,应用运行时需要
  • devDependencies:开发环境依赖,仅开发时需要
  • peerDependencies:对等依赖,要求用户自行安装
  • optionalDependencies:可选依赖,安装失败也不影响应用运行
  • bundleDependencies:捆绑依赖,发布时会包含在包中

6.2 依赖树

当安装包时,npm会构建一个依赖树,处理包之间的依赖关系。

查看依赖树

# 查看依赖树
npm ls

# 查看特定包的依赖
npm ls lodash

# 查看扁平化的依赖
npm ls --prod --depth=0

依赖冲突

当不同包依赖同一个包的不同版本时,可能会发生依赖冲突。npm会通过以下方式处理:

  1. 嵌套安装:将不同版本的包安装在各自的node_modules目录中
  2. 扁平化:在可能的情况下,将依赖扁平化到顶层

6.3 package-lock.json

package-lock.json文件用于锁定依赖版本,确保在不同环境中安装相同版本的依赖。

作用

  • 锁定版本:记录每个依赖的确切版本
  • 锁定依赖树:记录完整的依赖树结构
  • 加速安装:基于锁定文件快速安装依赖

生成和更新

  • 当运行npm install时,会自动生成或更新package-lock.json
  • 当修改package.json中的依赖后,运行npm install会更新package-lock.json
  • 当使用npm ci时,会严格按照package-lock.json安装依赖

6.4 npm ci

npm ci(clean install)是一个用于CI/CD环境的命令,它会:

  1. 删除现有的node_modules目录
  2. 严格按照package-lock.json安装依赖
  3. 不更新package-lock.json
  4. npm install更快
# 使用npm ci安装依赖
npm ci

# 仅安装生产依赖
npm ci --only=production

7. 发布包

7.1 准备发布

创建账号

  1. npm官网注册账号
  2. 或使用命令行注册
npm adduser
# 或
npm login

配置包信息

确保package.json中的信息完整准确:

{
  "name": "your-package-name",
  "version": "1.0.0",
  "description": "包的描述",
  "main": "index.js",
  "scripts": {
    "test": "jest"
  },
  "keywords": ["keyword1", "keyword2"],
  "author": "Your Name <your.email@example.com>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/username/your-package.git"
  },
  "bugs": {
    "url": "https://github.com/username/your-package/issues"
  },
  "homepage": "https://github.com/username/your-package#readme"
}

创建README.md

# your-package-name

包的描述

## 安装

```bash
npm install your-package-name

使用

const yourPackage = require('your-package-name');

// 使用示例

API

方法1

描述

方法2

描述

许可证

MIT


### 7.2 发布包

```bash
# 检查包是否符合发布要求
npm publish --dry-run

# 发布包
npm publish

# 发布测试版本
npm version prerelease
npm publish --tag beta

# 发布到组织
npm publish --scope=@username

# 发布私有包
npm publish --access=private

7.3 更新包

更新版本

# 更新补丁版本(1.0.0 → 1.0.1)
npm version patch

# 更新次版本(1.0.0 → 1.1.0)
npm version minor

# 更新主版本(1.0.0 → 2.0.0)
npm version major

# 更新预发布版本(1.0.0 → 1.0.1-beta.1)
npm version prerelease

# 设置特定版本
npm version 1.2.3

发布更新

# 更新版本后发布
npm version patch
npm publish

7.4 撤销发布

# 撤销发布的包
npm unpublish your-package-name@1.0.0

# 撤销整个包(谨慎使用)
npm unpublish your-package-name --force

注意:撤销发布的包可能会影响依赖它的项目,应谨慎使用。

8. 高级功能

8.1 工作区(Workspaces)

工作区允许在一个仓库中管理多个相关的包,适用于monorepo项目。

配置工作区

{
  "name": "monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "build": "npm run build --workspaces",
    "test": "npm run test --workspaces"
  }
}

使用工作区

# 安装所有工作区的依赖
npm install

# 在特定工作区安装依赖
npm install lodash --workspace=packages/package1

# 运行特定工作区的脚本
npm run build --workspace=packages/package1

# 运行所有工作区的脚本
npm run build --workspaces

8.2 钩子(Hooks)

npm支持钩子,可以在特定事件发生时执行自定义脚本。

安装钩子

npm install husky --save-dev

配置钩子

# 初始化husky
npx husky install

# 添加钩子
npx husky add .husky/pre-commit "npm test"
npx husky add .husky/pre-push "npm run lint"

8.3 缓存管理

npm会缓存安装过的包,加速后续安装。

管理缓存

# 查看缓存大小
npm cache ls --summary

# 清理缓存
npm cache clean --force

# 验证缓存
npm cache verify

8.4 审计(Audit)

npm audit用于检查依赖中的安全漏洞。

# 运行审计
npm audit

# 自动修复漏洞
npm audit fix

# 仅修复生产依赖的漏洞
npm audit fix --only=prod

# 强制修复所有漏洞
npm audit fix --force

# 查看详细报告
npm audit --json > audit-report.json

8.5 shrinkwrap

npm shrinkwrap命令用于生成npm-shrinkwrap.json文件,锁定依赖版本,类似于package-lock.json

# 生成shrinkwrap文件
npm shrinkwrap

# 更新shrinkwrap文件
npm shrinkwrap --dev

注意package-lock.jsonnpm-shrinkwrap.json的超集,推荐使用package-lock.json

9. 最佳实践

9.1 项目配置

  1. **使用.gitignore**:忽略node_modulesnpm-debug.log等文件
  2. 锁定版本:使用package-lock.jsonnpm-shrinkwrap.json
  3. 指定Node.js版本:在package.json中设置engines字段
  4. 使用语义化版本:遵循SemVer规范

9.2 依赖管理

  1. 分离依赖类型:正确使用dependencies和devDependencies
  2. 定期更新依赖:使用npm outdated检查更新
  3. 避免全局安装:优先本地安装,使用npx运行命令
  4. 使用固定版本:生产环境使用固定版本,避免意外更新

9.3 脚本管理

  1. 使用有意义的脚本名称:如build、test、lint等
  2. 使用预脚本和后脚本:组织复杂任务
  3. 使用npm-run-all:运行多个脚本
  4. 使用cross-env:跨平台设置环境变量

9.4 安全性

  1. 定期审计:使用npm audit检查安全漏洞
  2. 使用安全的依赖:避免使用有已知漏洞的包
  3. 保护npm令牌:不要将_authToken提交到版本控制
  4. 使用私有registry:企业环境使用私有npm registry

9.5 性能优化

  1. 使用npm ci:CI/CD环境使用npm ci加速安装
  2. 清理缓存:定期清理npm缓存
  3. 使用pnpm:考虑使用pnpm提高安装速度
  4. 优化依赖树:减少不必要的依赖,避免版本冲突

10. 常见问题与解决方案

10.1 安装失败

问题:npm install失败

解决方案

  • 检查网络连接
  • 检查npm配置(如registry)
  • 清理缓存:npm cache clean --force
  • 删除node_modules和package-lock.json,重新安装
  • 使用管理员权限运行命令

10.2 依赖冲突

问题:依赖版本冲突

解决方案

  • 使用npm ls查看依赖树
  • 手动指定版本
  • 使用resolutions字段(Yarn)或overrides字段(npm 8+)
  • 考虑使用pnpm的依赖管理

10.3 脚本执行失败

问题:npm run脚本执行失败

解决方案

  • 检查脚本语法
  • 检查依赖是否安装
  • 检查环境变量
  • 查看详细错误信息

10.4 发布失败

问题:npm publish失败

解决方案

  • 检查包名是否已存在
  • 检查npm账号权限
  • 检查package.json配置
  • 检查网络连接

10.5 速度慢

问题:npm安装速度慢

解决方案

  • 使用国内镜像:npm config set registry https://registry.npmmirror.com
  • 清理缓存:npm cache clean --force
  • 使用pnpm或yarn
  • 考虑使用缓存服务(如npm cache)

11. 总结

npm是Node.js生态系统中不可或缺的工具,它不仅是一个包管理器,还是一个项目管理工具,提供了丰富的功能来简化开发流程。通过本教程的学习,您应该已经掌握了npm的基本用法和高级功能,可以在实际项目中高效地使用npm来管理依赖、运行脚本、发布包等。

11.1 核心优势

  • 丰富的包生态:访问世界上最大的开源软件注册表
  • 强大的依赖管理:处理复杂的依赖关系
  • 灵活的脚本系统:自动化各种开发任务
  • 简单的发布流程:轻松分享和分发代码
  • 广泛的社区支持:大量的文档和资源

11.2 适用场景

  • Web开发:管理前端和后端依赖
  • Node.js应用:构建和部署Node.js应用
  • 命令行工具:开发和发布CLI工具
  • 库开发:创建和发布可重用的库
  • Monorepo项目:管理多包项目

11.3 未来发展

npm持续演进,未来的发展趋势包括:

  • 更快的安装速度:优化依赖解析和安装过程
  • 更好的monorepo支持:增强工作区功能
  • 更强的安全性:改进漏洞检测和修复
  • 更丰富的CLI功能:提供更多实用命令
  • 更好的与现代工具集成:与ES模块、TypeScript等更好集成

通过不断学习和实践,您可以充分发挥npm的优势,提高开发效率,构建更高质量的项目。

« 上一篇 62. Node.js - 服务器端JavaScript运行时 下一篇 » Yarn 中文教程