pnpm 常用命令
pnpm(Performant npm)是一个快速、节省磁盘空间的包管理器,通过硬链接和符号链接共享依赖,避免重复安装,天然解决幽灵依赖问题。
安装
# 通过 npm 全局安装
npm install -g pnpm
# 通过 corepack 启用(Node.js 16.13+)
# Corepack 是 Node.js 内置的包管理器管理工具
corepack enable
corepack prepare pnpm@latest --activate
# 通过 Homebrew(macOS)
brew install pnpm
# 通过独立脚本(Linux/macOS)
curl -fsSL https://get.pnpm.io/install.sh | sh -
# 查看版本
pnpm -v项目初始化
# 初始化新项目
pnpm init
# 跳过问答,生成默认配置
# -y 表示 yes,所有选项使用默认值
pnpm init -y依赖管理
四种依赖类型说明
| 类型 | 安装命令 | 说明 | 大白话解释 | 使用场景 |
|---|---|---|---|---|
dependencies | pnpm add <pkg> | 生产环境依赖 | 项目运行必须用到的包 | vue、react、axios、lodash 等 |
devDependencies | pnpm add -D <pkg> | 开发环境依赖 | 只在开发时用,打包后不需要 | vite、eslint、typescript、jest 等 |
peerDependencies | pnpm add -P <pkg> | 对等依赖 | 你的包「期望」使用者自己安装的依赖 | 组件库期望使用者已安装 vue |
optionalDependencies | pnpm add -O <pkg> | 可选依赖 | 安装失败不会报错,项目照样能跑 | 跨平台兼容包、性能优化包 |
安装依赖
# 安装所有依赖(根据 pnpm-lock.yaml)
pnpm install
pnpm i # 简写,效果一样
# 安装指定包(添加到 dependencies)
pnpm add <package>
# 安装指定包(添加到 devDependencies)
# -D 是 --save-dev 的简写,表示只在开发环境使用
pnpm add -D <package>
# 安装指定版本
pnpm add <package>@1.2.3
# 安装最新版本
pnpm add <package>@latest
# 安装到 peerDependencies
# -P 是 --save-peer 的简写
pnpm add -P <package>
# 安装到 optionalDependencies
# -O 是 --save-optional 的简写
pnpm add -O <package>
# 全局安装
# -g 是 --global 的简写,全局安装的包可以在任何地方使用命令
pnpm add -g <package>
# 安装并锁定精确版本(不用 ^ 和 ~)
# --save-exact 表示精确版本,不加版本前缀
pnpm add <package>@1.2.3 --save-exact
# 安装 Git 仓库(直接从 GitHub 安装)
pnpm add github:user/repo
pnpm add git+https://github.com/user/repo.git
# 安装本地包(本地开发调试)
pnpm add file:../my-local-package
pnpm add ../my-local-package升级 & 卸载
# 升级指定包(遵守 package.json 中的版本范围)
pnpm update <package>
pnpm up <package> # 简写
# 升级到最新版本(忽略版本范围,直接装最新)
# --latest 表示忽略版本范围,使用 npm 上的最新版本
pnpm update <package> --latest
# 升级所有依赖
pnpm update
pnpm up # 简写
# 交互式升级(交互式界面,可以选择要升级的包)
pnpm update --interactive
pnpm up -i # 简写
# 卸载指定包
pnpm remove <package>
pnpm rm <package> # 简写
pnpm un <package> # 更短的简写
# 全局卸载
pnpm remove -g <package>查看依赖
# 列出已安装的包(树形结构)
pnpm list
pnpm ls # 简写
# 只列出顶层依赖(不显示嵌套的子依赖)
# --depth 控制显示层级,0 表示只显示顶层
pnpm ls --depth=0
# 列出全局安装的包
pnpm ls -g
# 查看某个包的详细信息(版本、依赖、仓库、作者等)
pnpm info <package>
pnpm view <package>
# 查看包的所有历史版本
pnpm info <package> versions
# 查看为什么安装了某个包(显示依赖链,知道是谁引入的)
pnpm why <package>
# 检查过期依赖(显示当前版本、期望版本、最新版本)
pnpm outdated运行脚本
# 运行 package.json 中定义的脚本
# pnpm 不需要 run 关键字,直接 pnpm <script-name>
pnpm <script-name>
pnpm run <script-name> # 完整写法
# 常见脚本
pnpm dev # 启动开发服务器
pnpm build # 构建生产版本
pnpm test # 运行测试
# 传递参数给脚本
# -- 是参数分隔符,用于区分 pnpm 本身的参数和脚本的参数
# -- 之后的所有参数都会传递给脚本
pnpm <script-name> -- <args>
# 可以传递多个参数
pnpm build -- --mode production --verbose
# 常见工具参数示例:
# Vite 构建工具:
pnpm dev -- --port 3001 # 指定开发服务器端口
pnpm dev -- --host # 允许外部访问(局域网其他设备可访问)
pnpm build -- --mode staging # 指定构建模式(staging 是预发布环境)
pnpm build -- --outDir dist2 # 指定输出目录
# ESLint 代码检查:
pnpm lint -- --fix # 自动修复可修复的问题
pnpm lint -- --ext .js,.ts # 指定检查的文件扩展名(ESLint 9+ 已废弃此参数)
pnpm lint -- --no-error-on-unmatched-pattern # 无匹配文件时不报错
# Vitest 测试框架:
pnpm test -- --watch # 监听模式,文件变化时重新测试
pnpm test -- --coverage # 生成测试覆盖率报告
pnpm test -- --reporter verbose # 使用详细报告器
pnpm test -- --bail 1 # 第一个测试失败后停止
# TypeScript 编译器:
pnpm build -- --noEmit # 只检查类型,不输出文件(常用于类型检查)
pnpm build -- --sourceMap # 生成 source map(调试时定位源码)
pnpm build -- --watch # 监听模式,文件变化时自动编译
# 查看所有可用脚本
pnpm run
# 并行执行多个脚本
# --parallel 表示并行执行,多个脚本同时运行
pnpm run --parallel dev build📖 创建自定义脚本的详细方法请参考 npm 常用命令
npx 替代:pnpm dlx
# 直接运行包中的命令(无需安装,临时下载执行)
pnpm dlx create-vite my-app
pnpm dlx eslint --init
pnpm dlx degit user/repo my-app
# 指定版本
pnpm dlx <package>@<version>依赖检查
# 安全漏洞检查
pnpm audit
# 查看审计报告(JSON 格式,便于程序处理)
pnpm audit --json
# 自动修复安全漏洞
pnpm audit fixCI/CD 安装
# CI/CD 推荐用法:先 fetch 再 install
# pnpm fetch 只下载 pnpm-lock.yaml 中记录的包,跳过解析 package.json,速度更快
pnpm fetch
# --offline 表示只从本地缓存安装,不联网下载
pnpm install --offline⚠️
--offline要求本地缓存中已有所有依赖包,否则会安装失败。CI 环境首次运行需先执行pnpm fetch填充缓存,或确保 CI 缓存了 pnpm store。
pnpm install vs pnpm fetch + pnpm install --offline
| 特性 | pnpm install | pnpm fetch + install --offline |
|---|---|---|
| 解析 package.json | 是 | 否(跳过) |
| 下载依赖 | 是 | 是(fetch 阶段) |
| 安装依赖 | 是 | 是(install 阶段) |
| 速度 | 较慢 | 更快 |
| 适用场景 | 本地开发 | CI/CD、自动化部署 |
通俗解释:
pnpm install:智能安装,会解析 package.json 并下载依赖,适合本地开发pnpm fetch+pnpm install --offline:分两步走,先下载再安装,跳过解析步骤,适合 CI/CD 环境
工作空间(Monorepo)
什么是 Monorepo?
Monorepo(单一仓库)是一种项目管理方式:把多个相关的项目(包)放在同一个代码仓库中管理。
传统方式(Multirepo):
repo-utils/ # 独立仓库
└── package.json
repo-components/ # 独立仓库
└── package.json
repo-app/ # 独立仓库
└── package.jsonMonorepo 方式:
my-project/ # 一个仓库
├── package.json # 根配置
├── packages/
│ ├── utils/ # 工具库
│ │ └── package.json
│ └── components/ # 组件库
│ └── package.json
└── apps/
├── web/ # Web 应用
│ └── package.json
└── admin/ # 后台管理
└── package.json为什么用 Monorepo?
| 场景 | 传统方式 | Monorepo |
|---|---|---|
| 多个项目共享代码 | 需要发布 npm 包再引用 | 直接引用本地包,实时生效 |
| 修改一个库需要同时改多个项目 | 需要修改多个仓库 | 一个仓库内统一修改 |
| 统一代码规范和构建工具 | 每个仓库单独配置 | 根目录统一配置 |
| 查看某次修改影响了哪些项目 | 需要跨仓库搜索 | 一个仓库内搜索 |
什么时候用 Monorepo?
适合:
- 前端组件库 + 多个使用该组件库的项目
- 后端微服务(多个服务共享工具库)
- 全栈项目(前端 + 后端 + 共享类型定义)
- 工具库开发(一个仓库维护多个 npm 包)
不适合:
- 完全独立的项目(没有共享代码)
- 团队成员不熟悉 Monorepo
- 项目非常大(Git 仓库超过几个 GB)
pnpm 工作空间配置
pnpm 对 monorepo 有出色的支持,通过 pnpm-workspace.yaml 文件配置:
# pnpm-workspace.yaml
packages:
- 'packages/*' # packages 目录下的所有子目录
- 'apps/*' # apps 目录下的所有子目录
- 'tools/*' # tools 目录下的所有子目录
- '!**/test/**' # 排除测试目录(! 表示排除)常用命令
# 安装所有工作空间的依赖(根目录执行一次即可,自动安装所有子项目的依赖)
pnpm install
pnpm i
# 在所有工作空间中执行命令
# -r 是 --recursive 的简写,表示递归执行
pnpm -r run build
pnpm -r build # 简写
# 在指定工作空间中执行
# --filter 按包名过滤
pnpm --filter <package-name> run build
pnpm -F <package-name> build # 简写
# 在匹配的工作空间中执行(支持通配符)
pnpm --filter "@my-org/*" run test
pnpm -F "@my-org/*" test
# 根据依赖关系排序执行(拓扑排序,先执行被依赖的包)
pnpm -r --sort run build
# 添加依赖到指定工作空间
pnpm --filter <package-name> add <dependency>
# 添加公共依赖到根目录
# -w 表示根目录(workspace root)
pnpm add -w -D <package>
# 在所有工作空间中添加依赖
pnpm -r add <package>
# 过滤执行(包含依赖项)
# ... 表示包含依赖项,先执行依赖项再执行当前包
pnpm --filter <package-name>... run build
# 只在有该脚本的工作空间执行(没有该脚本的跳过)
pnpm -r run build --if-present工作空间之间互相引用
pnpm 支持 workspace: 协议,明确引用本地包:
// apps/web/package.json
{
"dependencies": {
"@my-org/utils": "workspace:*", // 引用本地 utils,任何版本
"@my-org/components": "workspace:^1.0.0" // 引用本地 components,^1.0.0 范围
}
}workspace:* 表示使用本地最新版本,发布时会自动替换为实际版本号。
高级功能
pnpm patch(补丁修改)
什么是 patch?什么时候用?
当你使用的某个 npm 包有 bug,但上游还没修复时,你可以用 patch 直接修改 node_modules 中的包,并生成一个补丁文件。这个补丁会被记录在项目中,其他人安装依赖时会自动应用这个补丁。
典型场景:
- 第三方包有个小 bug,但作者迟迟不修复
- 需要临时修改某个包的行为,等上游修复后再移除
# 开始编辑某个包(会打开一个临时目录让你修改)
pnpm patch <package>
# 修改文件后,生成补丁文件
pnpm patch-commit <patch-dir>
# 补丁文件保存在 patches/ 目录
# patches/<package>+<version>.patchpnpm deploy(部署)
什么是 deploy?什么时候用?
在 Monorepo 中,你可能只想部署某个子包(比如只部署 apps/web),而不需要整个仓库。pnpm deploy 会把指定包及其依赖打包到一个目录中,适合 Docker 镜像构建、Serverless 部署等场景。
为什么不用直接复制?
- deploy 会自动分析依赖关系,只打包需要的文件
- 输出的目录可以直接运行,不需要再 install
# 将指定包部署到目标目录
pnpm --filter <package-name> deploy ./deploy-outputpnpm fetch(CI 优化)
什么是 fetch?什么时候用?
pnpm fetch 是 CI/CD 专用命令。它只下载 pnpm-lock.yaml 中记录的包,跳过解析 package.json 的步骤,速度更快且保证安装结果与本地完全一致。
为什么不用 pnpm install?
- fetch 跳过了依赖解析步骤,只做下载,更快
- 严格按 lock 文件安装,不会意外更新依赖
# CI/CD 推荐用法
pnpm fetch
pnpm install --offlinepnpm-lock.yaml 文件
什么是 pnpm-lock.yaml 文件?
pnpm-lock.yaml 是 pnpm 自动生成的依赖锁定文件,记录了每个依赖的精确版本和下载地址。
为什么需要 pnpm-lock.yaml?
| 问题 | 没有 lock 文件 | 有 lock 文件 |
|---|---|---|
| 版本不一致 | 不同环境安装的版本可能不同 | 所有环境安装完全相同的版本 |
| 构建失败 | 依赖更新导致构建失败 | 依赖版本固定,构建稳定 |
| 协作问题 | 团队成员依赖版本不一致 | 团队成员使用相同版本 |
pnpm-lock.yaml 必须提交到 Git
# .gitignore 中不要忽略 pnpm-lock.yaml
# 但要忽略 node_modules
node_modules/pnpm-lock.yaml 冲突解决
# 方案一:删除重新安装(简单粗暴,推荐新手)
rm -rf node_modules pnpm-lock.yaml
pnpm install
# 方案二:使用 Git 合并后重新安装
git checkout --theirs pnpm-lock.yaml
pnpm install缓存管理
# 查看缓存目录路径
pnpm store path
# 查看缓存状态(哪些包被缓存了)
pnpm store status
# 清除未使用的缓存(删除不再需要的包)
pnpm store prune
# 查看全局存储中的包
pnpm store list缓存的作用
- 提升安装速度:已下载的包会缓存,下次安装直接从缓存读取
- 节省磁盘空间:pnpm 使用硬链接,多个项目共享同一份缓存
- 离线安装:有缓存时可以离线安装(部分场景)
什么时候需要清理缓存?
- 安装失败且提示缓存相关错误
- 缓存占用空间过大
- 切换镜像源后需要清理旧缓存
配置管理
# 查看所有配置
pnpm config list
# 设置镜像源(国内用户推荐使用淘宝镜像)
pnpm config set registry https://registry.npmmirror.com
# 查看当前镜像源
pnpm config get registry
# 恢复默认镜像源
pnpm config set registry https://registry.npmjs.org
# 编辑配置文件
pnpm config edit.npmrc 配置
pnpm 兼容 .npmrc 配置文件:
# .npmrc
# 镜像源配置(国内用户推荐使用淘宝镜像)
registry=https://registry.npmmirror.com
# 依赖提升策略
# true:将所有依赖提升到根 node_modules(失去严格隔离优势,慎用)
shamefully-hoist=true
# 严格模式(默认开启,禁止未声明的依赖)
# false:允许未声明的依赖(不推荐)
strict-peer-dependencies=false
# 自动安装 Peer Dependencies
auto-install-peers=true
# 排除的包(不提升到根 node_modules)
# 只提升匹配的包到根 node_modules
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
# 忽略生命周期脚本(安全考虑,防止恶意脚本)
ignore-scripts=false
# 存储目录(全局缓存位置)
store-dir=~/.pnpm-store
# 虚拟存储目录(node_modules 中的 .pnpm 目录)
virtual-store-dir=node_modules/.pnpmpnpm 配置项
# 设置并发数(同时下载的包数量,网络好可以设大)
pnpm config set network-concurrency 16
# 设置超时(下载包的超时时间,单位毫秒)
pnpm config set fetch-timeout 60000
# 设置重试次数(下载失败后的重试次数)
pnpm config set fetch-retries 5
# 锁文件(是否自动生成 pnpm-lock.yaml)
pnpm config set lockfile true
# 忽略 pnpm-lock.yaml 中的时间戳(解决冲突时有用)
pnpm config set lockfile-allow-timestamps truepnpm 与其他包管理器的区别
| 特性 | npm | yarn | pnpm | cnpm |
|---|---|---|---|---|
| 安装速度 | 慢 | 中等 | 快 | 快(国内) |
| 磁盘占用 | 大 | 大 | 小(硬链接) | 大 |
| 依赖提升 | 扁平化 | 扁平化 | 严格隔离 | 扁平化 |
| Monorepo 支持 | 一般 | 好 | 优秀 | 一般 |
| 锁文件 | package-lock.json | yarn.lock | pnpm-lock.yaml | package-lock.json(9.x 支持) |
| 幽灵依赖 | 存在 | 存在 | 不存在 | 存在 |
| node_modules 结构 | 扁平 | 扁平 | 符号链接 + 硬链接 | 扁平 |
| CI 缓存 | npm ci | --frozen-lockfile | pnpm fetch | npm ci --registry=... |
幽灵依赖问题
npm 和 yarn 的扁平化 node_modules 结构会导致幽灵依赖——你可以引用没有在 package.json 中声明的依赖。pnpm 使用符号链接,严格隔离,只有声明的依赖才能被引用。
node_modules 结构对比
npm/yarn(扁平化):
node_modules/
├── a/
├── b/ ← 幽灵依赖(未声明但可用)
├── c/
└── node_modules/
└── d/ ← 嵌套依赖
pnpm(严格隔离):
node_modules/
├── .pnpm/ ← 全局存储的硬链接
│ ├── [email protected]/
│ ├── [email protected]/
│ └── [email protected]/
├── a → .pnpm/[email protected]/node_modules/a ← 符号链接
├── b → .pnpm/[email protected]/node_modules/b
└── c → .pnpm/[email protected]/node_modules/c常见问题
切换包管理器
# 从 npm 切换到 pnpm
rm -rf node_modules package-lock.json
pnpm install
# 从 yarn 切换到 pnpm
rm -rf node_modules yarn.lock
pnpm install安装速度慢
# 使用淘宝镜像源(推荐国内用户)
pnpm config set registry https://registry.npmmirror.com依赖提升问题
某些包可能因为严格隔离而报错,可以配置提升策略:
# 在 .npmrc 中设置(全局提升,慎用)
shamefully-hoist=true
# 推荐:只提升特定包
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*⚠️
shamefully-hoist=true会将依赖提升到根node_modules,失去严格隔离的优势,仅在必要时使用。
锁文件冲突
# 删除重新生成(简单粗暴,推荐新手)
rm -rf node_modules pnpm-lock.yaml
pnpm install
# 或者使用 git 合并后重新安装
git checkout --theirs pnpm-lock.yaml
pnpm install旧版本 Node.js 兼容
# pnpm 8+ 需要 Node.js 16+
# 使用 corepack 指定 pnpm 版本
corepack prepare pnpm@7 --activate