pnpm workspace 如何配置和使用?
pnpm workspace 是 pnpm 内置的 monorepo 方案,让你在一个仓库里管理多个互相依赖的包。相比 yarn workspaces 和 lerna,它零额外依赖、硬链接共享磁盘空间,配置也最简单。
如何声明 workspace
在项目根目录创建 pnpm-workspace.yaml:
yamlpackages: - 'packages/*' - 'apps/*' - 'shared/*'
根目录的 package.json 必须设置 "private": true,防止根包被误发布。
典型目录结构
shellmy-monorepo/ ├── pnpm-workspace.yaml ├── package.json # private: true ├── pnpm-lock.yaml ├── .npmrc # pnpm 专用配置 ├── packages/ │ ├── ui/ │ │ ├── package.json # name: @my-org/ui │ │ └── src/ │ └── utils/ │ ├── package.json # name: @my-org/utils │ └── src/ └── apps/ ├── web/ │ ├── package.json # name: @my-org/web │ └── src/ └── server/ ├── package.json └── src/
包间依赖如何引用
使用 workspace: 协议引用本地包,开发时直接链接源码,不用发布再安装:
json// apps/web/package.json { "name": "@my-org/web", "dependencies": { "@my-org/ui": "workspace:*", "@my-org/utils": "workspace:^1.0.0" } }
workspace: 后面的版本写法决定了发布时的替换规则:
| 协议写法 | 开发时行为 | 发布时替换为 |
|---|---|---|
workspace:* | 链接最新 | 精确版本如 1.2.3 |
workspace:^ | 链接最新 | ^1.2.3 |
workspace:~ | 链接最新 | ~1.2.3 |
workspace:^1.0.0 | 链接最新 | ^1.0.0(保留原范围) |
发布时 workspace: 会被自动替换为实际版本号,这是 pnpm 的内置行为,不需要额外配置。
常用命令速查
bash# 安装所有包的依赖 pnpm install # 在指定包中执行命令(--filter 或 -F) pnpm --filter @my-org/ui build pnpm -F @my-org/ui build # 递归执行:所有包都跑 build pnpm -r build # 只构建有变更的包(基于 git diff) pnpm -r --filter "...[origin/main]" build # 给指定包添加依赖 pnpm --filter @my-org/web add @my-org/ui # 给根目录添加公共开发依赖(-w 标志) pnpm add -Dw eslint prettier
-r(递归)和 --filter 的区别:-r 对所有包执行,--filter 按条件筛选。生产环境推荐用 --filter 精确控制,避免无关包被意外构建。
.npmrc 关键配置
pnpm workspace 的许多行为可以通过 .npmrc 调整:
ini# 未在 workspace 找到的包是否从 registry 下载(默认 true) link-workspace-packages=true # 依赖提升策略(避免幽灵依赖) shamefully-hoist=false node-linker=hoisted # 需要 hoist 时用这个,不推荐 # 严格对等依赖 strict-peer-dependencies=true
link-workspace-packages=true 配合 workspace:* 使用时,如果本地包版本满足范围就链接本地,否则从 registry 下载。这在逐步迁移 monorepo 时很有用。
用 changesets 管理版本和发布
多包版本管理推荐用 changesets,它是 pnpm 官方推荐的方案:
bash# 安装 pnpm add -Dw @changesets/cli pnpm changeset init # 工作流 pnpm changeset # 交互式记录本次变更(选包、选版本类型、写 changelog) pnpm changeset version # 根据记录更新 package.json 和 CHANGELOG.md pnpm -r publish # 发布所有有新版本的包
changeset init 会生成 .changeset/ 目录和 config.json,其中可以配置 changelog 格式、access(public/restricted)等。
常见问题
Q: 修改了子包代码,依赖它的包要重新安装吗?
不需要。workspace:* 链接的是源码,修改即生效(前提是需要重新 build 的包要重新构建)。
Q: 发布时 workspace: 协议怎么处理?
pnpm publish 时 workspace:* 会自动替换为 package.json 中的实际版本号。如果版本号不存在会报错。
Q: 怎么排查某个包的依赖关系?
bashpnpm list --filter @my-org/web --depth 1 # 查看依赖树 pnpm why @my-org/ui --filter @my-org/web # 查看为什么依赖这个包
Q: 子包之间循环依赖怎么办?
pnpm 会报错。解决方式是抽取共享逻辑到第三个包,或者通过接口/事件解耦。
pnpm workspace vs 其他方案
| 特性 | pnpm workspace | Yarn Workspaces | Lerna | Turborepo |
|---|---|---|---|---|
| 内置支持 | 是 | 是 | 需安装 | 需安装 |
| 依赖存储 | 硬链接(全局 store) | 符号链接 | 各自安装 | 依赖 pnpm/yarn |
| 磁盘效率 | 最高 | 中等 | 最低 | 取决于底层 |
| 任务缓存 | 否 | 否 | 否 | 是 |
| 配置复杂度 | 低 | 低 | 高 | 中 |
| 版本管理 | 需配合 changesets | 需配合工具 | 内置 | 需配合工具 |
pnpm workspace 本身只解决依赖管理和包链接,任务编排(缓存、并行)交给 Turborepo 或 Nx,版本发布交给 changesets——这是目前最主流的组合方案。