全部文章

pnpm 与 Monorepo

pnpm 使用技巧、workspace 配置、Monorepo 项目管理与 Turborepo

目录 18 节

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 按影响范围执行

延伸阅读

参考链接

阅读建议
  • - 先读标题和摘要,再结合目录决定从哪个章节开始精读。
  • - 看到具体命令、配置或步骤时,尽量在自己的环境里同步验证。
  • - 这类文档通常和本地环境有关,建议同时打开终端或编辑器对照操作。
适合谁看
  • - 希望把零散经验整理成长期可复用工作流的人
  • - 正在搭建开发环境、统一工具链或排查构建问题的开发者
  • - 希望阅读时顺手建立自己的操作清单或收藏体系的人
执行前检查
  • - 先浏览标题、摘要和目录,带着问题阅读会更高效
  • - 确认本机 Node、包管理器、终端和仓库依赖版本是否一致
  • - 如果页面里提到相关文档,尽量一起打开对照,效果通常更完整
同类内容
← 上一篇OpenClaw - 个人 AI 助手网关