📦 pnpm 使用教程 — Node.js 包管理终极指南
pnpm 是当前最快的 Node.js 包管理器,采用内容寻址存储,比 npm/yarn 更快、更省磁盘空间。它严格管理依赖关系,消除幽灵依赖问题,原生支持 monorepo workspace,是现代 Node.js 项目的最佳选择。
一、安装 pnpm
# 官方脚本(推荐)
curl -fsSL https://get.pnpm.io/install.sh | sh -
# 通过 npm
npm install -g pnpm
# 通过 Homebrew (macOS)
brew install pnpm
# 通过 Corepack (Node.js 16.13+)
corepack enable
corepack prepare pnpm@latest --activate
# 验证安装
pnpm --version💡 推荐 Corepack,团队协作时自动管理版本,避免 lockfile 冲突。
二、为什么选择 pnpm?
vs npm / yarn
| 特性 | npm | yarn | pnpm |
|---|---|---|---|
| 安装速度 | 慢 | 中等 | 最快 |
| 磁盘占用 | 高 | 高 | 极低 |
| node_modules | 扁平 | 扁平 | 严格嵌套 |
| 幽灵依赖 | 存在 | 存在 | 不存在 |
| Monorepo | 一般 | 好 | 最佳 |
内容寻址存储
pnpm 将包缓存到全局 store,通过硬链接引用而非复制。100 个项目用 lodash 只占 1 份空间。
严格的 node_modules 结构
node_modules/
├── .pnpm/ # 所有依赖的硬链接存储
├── lodash -> .pnpm/lodash@4.17.21/...
├── express -> .pnpm/express@4.18.2/...
└── your-dependency -> ...你的代码只能访问直接声明的依赖,彻底消灭幽灵依赖。
三、基本命令
# 初始化项目
pnpm init
pnpm create vite my-app --template react-ts
# 安装依赖
pnpm install # 安装全部依赖
pnpm i # 缩写
# 添加依赖
pnpm add express # 生产依赖
pnpm add -D typescript eslint # 开发依赖
pnpm add react@18.2.0 # 指定版本
pnpm add "express@^4.18.0" # 版本范围
pnpm add -g pm2 # 全局安装
# 移除依赖
pnpm remove express
pnpm rm lodash四、版本指定符与依赖类型
版本指定符
pnpm add express@4.18.2 # 精确版本
pnpm add "express@^4.18.0" # 兼容: >=4.18.0 <5.0.0
pnpm add "express@~4.18.0" # 最小: >=4.18.0 <4.19.0
pnpm add next@canary # 发布渠道依赖类型
{
"dependencies": { "express": "^4.18.0" },
"devDependencies": { "typescript": "^5.0.0" },
"optionalDependencies": { "fsevents": "^2.3.0" },
"peerDependencies": { "react": "^18.0.0" }
}pnpm add express # dependencies
pnpm add -D typescript # devDependencies
pnpm add -O fsevents # optionalDependencies
pnpm install --prod # 跳过 dev 依赖💡 所有@types/*、测试框架、lint 工具用-D放进 devDependencies。
五、Workspace / Monorepo
pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"Workspace 协议
{
"dependencies": {
"@myorg/shared": "workspace:*",
"@myorg/utils": "workspace:^1.0.0"
}
}根 package.json
{
"name": "my-monorepo",
"private": true,
"scripts": {
"build": "pnpm -r run build",
"dev:web": "pnpm --filter web dev"
}
}💡 monorepo 推荐:包名用@org/前缀,workspace:*引用,根目录统一管理 dev 依赖。
六、脚本与生命周期钩子
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"test": "vitest",
"lint": "eslint .",
"prebuild": "pnpm lint",
"postbuild": "echo Done",
"prepare": "husky install"
}
}pnpm build # 运行脚本(run 可省略)
pnpm test -- --watch # 传递参数
pnpm exec eslint . # 执行 bin 命令
pnpm dlx create-vite # 临时执行(类似 npx)七、pnpm Store 管理
pnpm store path # 查看 store 路径: ~/.local/share/pnpm/store/v3
pnpm store prune # 清理不再使用的包
pnpm store status # 查看 store 状态# .npmrc - 自定义 store 路径
store-dir = /data/pnpm-store八、Lockfile (pnpm-lock.yaml)
- 记录精确依赖树、版本、完整性校验
- 跨平台兼容
- 必须提交到 Git
pnpm install # 自动更新 lockfile
pnpm install --frozen-lockfile # CI 中验证一致性
pnpm install --lockfile-only # 仅更新 lockfile九、.npmrc 配置
# .npmrc
registry=https://registry.npmmirror.com/
strict-peer-dependencies=true
auto-install-peers=true
save-workspace-protocol=true
enable-pre-post-scripts=true// package.json 中的 pnpm 配置
{
"pnpm": {
"overrides": { "semver": "^7.5.0" },
"patchedDependencies": {},
"peerDependencyRules": {
"ignoreMissing": ["@babel/*"]
}
}
}十、过滤与跨包运行
# 特定包
pnpm --filter web dev
pnpm --filter "@myorg/*" build
# 依赖拓扑
pnpm --filter ...web build # web 及其依赖
pnpm --filter web... build # web 及依赖 web 的包
# 递归执行
pnpm -r build # 所有包
pnpm -r --workspace-concurrency=4 build # 并行
pnpm -r exec rm -rf dist # 执行任意命令十一、CI/CD(GitHub Actions)
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm test
- run: pnpm build// package.json 锁定版本
{ "packageManager": "pnpm@10.4.1" }十二、Docker 多阶段构建
FROM node:20-alpine AS base
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
FROM base AS deps
COPY pnpm-lock.yaml package.json ./
RUN pnpm install --frozen-lockfile
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/pnpm-lock.yaml /app/package.json ./
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm install --prod
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]💡 先复制 lockfile + package.json 安装依赖,利用 Docker 缓存层。
十三、从 npm/yarn 迁移
# 删除旧文件
rm -rf node_modules package-lock.json # 或 yarn.lock
# pnpm 安装
pnpm install
# 处理幽灵依赖问题:显式添加缺失的包
pnpm add <缺失的包>
# 而不是开启 shamefully-hoist(不推荐)十四、npm 生态兼容
overrides(解决版本冲突/安全漏洞)
{
"pnpm": {
"overrides": {
"semver": "^7.5.0",
"express>@types/node": "^20.0.0"
}
}
}patch(修补第三方包)
pnpm patch some-package@1.0.0 # 创建补丁
# 编辑文件后
pnpm patch-commit <tmp目录> # 保存补丁十五、常用技巧
# 查看依赖
pnpm ls # 直接依赖
pnpm ls --depth=10 # 所有嵌套依赖
pnpm outdated # 过期依赖
pnpm up -i # 交互式更新
# 安全审计
pnpm audit
pnpm audit --fix
# 清理
pnpm store prune # 清理 store
rm -rf node_modules pnpm-lock.yaml && pnpm install # 完全重装
# 发布
pnpm publish
pnpm --filter @myorg/shared publish实用别名(.zshrc / .bashrc)
alias pi="pnpm install"
alias pa="pnpm add"
alias pad="pnpm add -D"
alias pb="pnpm build"
alias pd="pnpm dev"十六、命令速查表
| 命令 | 说明 |
|---|---|
pnpm init | 初始化项目 |
pnpm i | 安装依赖 |
pnpm add <pkg> | 添加依赖 |
pnpm add -D <pkg> | 添加开发依赖 |
pnpm rm <pkg> | 移除依赖 |
pnpm up | 更新依赖 |
pnpm run <script> | 运行脚本 |
pnpm exec <cmd> | 执行命令 |
pnpm dlx <cmd> | 临时执行(npx) |
pnpm -r <script> | 递归运行 |
pnpm --filter <pkg> | 过滤运行 |
pnpm store prune | 清理 store |
pnpm ls | 查看依赖 |
pnpm audit | 安全审计 |
pnpm publish | 发布包 |
十七、常见问题
Q: pnpm 和 npm 的区别是什么?
pnpm 更快、更省空间,严格 node_modules 消除幽灵依赖,原生 monorepo 支持。
Q: 幽灵依赖问题怎么解决?
显式添加缺失的包到 package.json,而不是开启 shamefully-hoist。
Q: pnpm-lock.yaml 冲突怎么办?
直接运行 pnpm install 自动合并,失败则删除 lockfile 重新生成。
Q: 如何在 CI 中加速?
使用 cache: "pnpm" + --frozen-lockfile。
Q: monorepo 中如何共享配置?
将通用 dev 依赖(@types、eslint、typescript)放在根目录。
参考链接
- 官方文档:https://pnpm.io/zh/
- GitHub:https://github.com/pnpm/pnpm
- npm 镜像:https://npmmirror.com/
- Workspace 教程:https://pnpm.io/zh/workspaces
- 迁移指南:https://pnpm.io/zh/migration
评论已关闭