pnpm 与 Monorepo
这页适合做“前端 / 全栈工作区工程化起步指南”。pnpm 很适合单仓库,也很适合 Monorepo,但真正要先想清楚的不是工具命令,而是:你到底有没有共享包、统一构建和跨项目协作的需求。
什么时候适合 Monorepo
下面几类情况比较适合:
- 有多个应用共享 UI 组件、工具包、类型定义
- Web、API、脚本、文档站希望统一管理
- 团队希望统一依赖版本、脚本入口和 CI 流程
- 需要按变更影响范围做增量构建
如果当前只有一个应用、没有共享代码、团队也不大,单仓库先保持简单往往更划算。
pnpm 基础
pnpm 使用硬链接和内容寻址存储,节省磁盘空间且安装速度快。
安装
# Windows
scoop install pnpm
# 或
winget install pnpm.pnpm
# 或通过 npm
npm install -g pnpm
常用命令
pnpm add express # 添加依赖
pnpm add -D vitest # 开发依赖
pnpm add -g tsx # 全局安装
pnpm remove express # 移除
pnpm install # 安装所有依赖
pnpm update # 更新
pnpm update --latest # 更新到最新(忽略 semver)
pnpm store prune # 清理未引用的包
pnpm why express # 查看为什么安装了某个包
.npmrc 配置
# .npmrc
shamefully-hoist=true # 提升依赖(某些包需要)
strict-peer-dependencies=false
auto-install-peers=true
Monorepo(pnpm workspace)
目录结构
my-monorepo/
├── package.json
├── pnpm-workspace.yaml
├── packages/
│ ├── shared/
│ │ └── package.json
│ ├── web/
│ │ └── package.json
│ └── api/
│ └── package.json
└── apps/
└── admin/
└── package.json
pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
推荐组织方式
一个比较常见、也比较好理解的约定是:
apps/放可独立运行的应用packages/放共享组件、工具、类型、配置- 根目录只放工作区级脚本和统一配置
这样做的好处是:开发者一眼就能分辨“这是产品应用”还是“这是被复用的基础包”。
工作区间依赖
# 在 web 包中添加 shared 作为依赖
pnpm add @myorg/shared --filter web
# 或在 package.json 中
{
"dependencies": {
"@myorg/shared": "workspace:*"
}
}
过滤命令
pnpm --filter web dev # 只在 web 包运行
pnpm --filter web... build # web 及其依赖
pnpm --filter ./packages/* test # 所有 packages 下的包
pnpm -r build # 所有包递归执行
过滤命令是 Monorepo 日常效率的关键,尤其适合:
- 只重建当前改动应用
- 只测试共享包
- CI 中按受影响范围减少无意义执行
Turborepo
Turborepo 为 Monorepo 提供增量构建和缓存。
pnpm add -Dw turbo
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"],
},
"dev": {
"cache": false,
"persistent": true,
},
"test": {
"dependsOn": ["build"],
},
"lint": {},
},
}
turbo build # 构建所有包(带缓存)
turbo dev --filter web # 开发指定包
turbo test --affected # 只测试受影响的包
常见问题
依赖装得上,但运行时报错
Monorepo 下最常见的问题之一是包边界不清晰。要先确认:
- 依赖是不是加到了正确的包里
- 共享包是不是正确声明了
dependencies/peerDependencies - 是否误依赖了某个包被 hoist 后“碰巧可用”的内容
工作区引用版本混乱
内部包建议优先使用:
{
"dependencies": {
"@myorg/shared": "workspace:*"
}
}
这样更明确,也更适合统一发布与升级。
CI 很慢
Monorepo 不代表必须全量跑。更推荐:
- 利用
pnpm --filter - 使用 Turborepo 缓存
- 让 lint / test / build 按影响范围执行
延伸阅读
参考链接
- pnpm — 官方中文文档
- Turborepo — 构建系统
- Monorepo Tools — 工具对比