跳到主要内容

npm 包管理

npm(Node Package Manager)是 Node.js 的默认包管理器,也是世界上最大的软件注册表。本章介绍 npm 的核心功能和使用技巧。

npm 简介

什么是 npm

npm 提供三个主要功能:

  1. 包注册表:托管开源 JavaScript 包
  2. 命令行工具:安装和管理包
  3. 包开发平台:发布和维护自己的包

npm vs yarn vs pnpm

特性npmyarnpnpm
安装速度中等最快
磁盘空间每个项目独立安装每个项目独立安装全局存储+符号链接
幽灵依赖
lock 文件package-lock.jsonyarn.lockpnpm-lock.yaml
monorepoworkspacesworkspacesworkspace + 内置支持

package.json 详解

创建 package.json

# 交互式创建
npm init

# 使用默认值创建
npm init -y

# 使用预设配置创建
npm init -y --scope=@myorg

完整配置示例

{
"name": "@myorg/my-package",
"version": "1.0.0",
"description": "一个示例包",
"keywords": ["nodejs", "example"],
"homepage": "https://github.com/myorg/my-package#readme",
"bugs": {
"url": "https://github.com/myorg/my-package/issues"
},
"license": "MIT",
"author": {
"name": "Your Name",
"email": "[email protected]",
"url": "https://example.com"
},
"contributors": [],
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./utils": {
"import": "./dist/utils.mjs",
"require": "./dist/utils.js"
}
},
"files": ["dist", "README.md", "LICENSE"],
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js",
"build": "tsc",
"test": "node --test",
"lint": "eslint src/",
"format": "prettier --write .",
"prepare": "husky install"
},
"dependencies": {
"express": "^4.18.2",
"lodash": "^4.17.21"
},
"devDependencies": {
"typescript": "^5.0.0",
"eslint": "^8.50.0",
"prettier": "^3.0.0"
},
"peerDependencies": {
"react": ">=17.0.0"
},
"optionalDependencies": {
"fsevents": "^2.3.0"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
},
"os": ["linux", "darwin"],
"cpu": ["x64", "arm64"],
"repository": {
"type": "git",
"url": "git+https://github.com/myorg/my-package.git"
},
"type": "module",
"sideEffects": false
}

依赖类型

类型用途安装命令
dependencies生产环境依赖npm install pkg
devDependencies开发环境依赖npm install -D pkg
peerDependencies对等依赖手动添加
optionalDependencies可选依赖手动添加
bundledDependencies打包依赖手动添加

版本号规则

npm 使用语义化版本(SemVer):主版本.次版本.补丁版本

版本范围符号:

符号含义示例
^兼容版本(不改变主版本)^1.2.3>=1.2.3 <2.0.0
~近似版本(不改变次版本)~1.2.3>=1.2.3 <1.3.0
> / >=大于/大于等于>1.2.3
< / <=小于/小于等于<2.0.0
``
-范围1.0.0 - 2.0.0
*任意版本*
latest最新版本latest

常用命令

安装依赖

# 安装所有依赖
npm install

# 安装生产依赖
npm install express

# 安装开发依赖
npm install -D typescript

# 安装指定版本
npm install [email protected]

# 安装最新版本
npm install express@latest

# 全局安装
npm install -g nodemon

# 安装特定范围的版本
npm install express@">=4.0.0 <5.0.0"

卸载依赖

# 卸载依赖
npm uninstall express

# 卸载开发依赖
npm uninstall -D typescript

# 卸载全局包
npm uninstall -g nodemon

更新依赖

# 检查可更新的包
npm outdated

# 更新所有依赖(在版本范围内)
npm update

# 更新到最新版本(可能超出范围)
npm install express@latest

# 全局更新
npm update -g

查看包信息

# 查看包信息
npm view express

# 查看特定版本信息
npm view [email protected]

# 查看所有可用版本
npm view express versions

# 查看包的依赖
npm view express dependencies

# 查看本地安装的包
npm list

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

运行脚本

# 运行脚本
npm run start
npm run build
npm run test

# 简写(仅限特定脚本)
npm start
npm test

# 传递参数
npm run test -- --coverage

# 并行/串行运行多个脚本
npm-run-all build:*

npm scripts 进阶

生命周期脚本

{
"scripts": {
"preinstall": "echo '安装前执行'",
"postinstall": "echo '安装后执行'",
"prepare": "npm run build",
"prestart": "echo '启动前执行'",
"start": "node index.js",
"poststart": "echo '启动后执行'"
}
}

跨平台脚本

使用跨平台工具替代平台特定命令:

{
"scripts": {
"clean": "rimraf dist",
"copy": "cpy src/** dist",
"env": "cross-env NODE_ENV=production",
"mkdir": "mkdirp dist"
},
"devDependencies": {
"rimraf": "^5.0.0",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.0",
"mkdirp": "^3.0.0"
}
}

钩子脚本

{
"scripts": {
"lint": "eslint src/",
"test": "node --test",
"precommit": "lint-staged",
"prepush": "npm test"
},
"devDependencies": {
"husky": "^8.0.0",
"lint-staged": "^14.0.0"
}
}

发布包

准备发布

  1. 注册 npm 账号
# 注册
npm adduser

# 或登录已有账号
npm login
  1. 配置 package.json
{
"name": "my-awesome-package",
"version": "1.0.0",
"main": "dist/index.js",
"files": ["dist", "README.md", "LICENSE"]
}
  1. 创建 .npmignore
# 忽略不需要发布的文件
src/
tests/
.*rc
*.log
node_modules/

发布流程

# 检查将要发布的文件
npm pack --dry-run

# 发布
npm publish

# 发布到私有仓库
npm publish --registry=https://registry.mycompany.com

# 发布预发布版本
npm publish --tag beta

# 发布 scoped 包(默认私有)
npm publish --access public

版本管理

# 更新补丁版本 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

# 预发布版本
npm version prerelease --preid=beta # 1.0.0 -> 1.0.1-beta.0

# 自定义提交信息
npm version patch -m "Upgrade to %s for reasons"

撤销发布

# 弃用包
npm deprecate my-package "此包已弃用,请使用 new-package"

# 删除版本(发布 24 小时内)
npm unpublish [email protected]

# 删除整个包
npm unpublish my-package --force

Workspaces(工作区)

npm 7+ 支持 monorepo:

{
"name": "my-monorepo",
"workspaces": [
"packages/*"
]
}

目录结构:

my-monorepo/
├── package.json
├── packages/
│ ├── core/
│ │ └── package.json
│ └── cli/
│ └── package.json
└── node_modules/
└── @my-org/
├── core -> ../../packages/core
└── cli -> ../../packages/cli

工作区命令:

# 在特定工作区执行命令
npm run test --workspace=packages/core

# 在所有工作区执行
npm run test --workspaces

# 安装依赖到特定工作区
npm install lodash -w packages/core

.npmrc 配置

创建 .npmrc 文件配置 npm:

# 镜像源
registry=https://registry.npmmirror.com

# 私有包配置
@mycompany:registry=https://npm.mycompany.com
//npm.mycompany.com/:_authToken=${NPM_TOKEN}

# 代理配置
proxy=http://proxy.company.com:8080
https-proxy=http://proxy.company.com:8080

# 缓存配置
cache=/path/to/cache

# 其他配置
save-exact=true
package-lock=false
engine-strict=true

常见问题

依赖冲突

# 查看依赖树
npm ls express

# 查看重复依赖
npm dedupe

# 强制重新安装
rm -rf node_modules package-lock.json
npm install

权限问题

# 修改 npm 默认目录
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'

# 添加到 PATH
export PATH=~/.npm-global/bin:$PATH

清除缓存

# 清除缓存
npm cache clean --force

# 验证缓存
npm cache verify

小结

本章我们学习了:

  1. package.json 配置:依赖类型、版本号、scripts
  2. 常用命令:安装、卸载、更新、查看包信息
  3. npm scripts:生命周期、跨平台、钩子
  4. 发布包:准备、发布、版本管理
  5. Workspaces:monorepo 支持
  6. 配置管理:.npmrc 文件

练习

  1. 创建一个 npm 包并发布到 npm(可使用 scoped 包)
  2. 配置 husky 和 lint-staged 实现提交前检查
  3. 使用 workspaces 创建一个 monorepo 项目
  4. 实现一个 CLI 工具并通过 npm 全局安装使用