FumadocsZDecode
Nodejs

Monorepo

在单一代码仓库中管理多个子项目

pnpm Workspaces 允许在单个代码仓库中管理多个子项目,子项目之间可以直接互相引用,共享依赖安装在根目录,避免重复下载。

适合以下场景:

  • 多个应用共用同一套 UI 组件或业务逻辑
  • 开发工具库时,将核心代码、测试、示例和文档统一管理

目录结构

my-monorepo/
├── apps/
│   ├── web/          # 应用 A
│   └── mobile/       # 应用 B
├── packages/
│   ├── ui/           # 共享 UI 组件
│   └── utils/        # 共享工具函数
├── pnpm-workspace.yaml
└── package.json

搭建步骤

1. 创建 pnpm-workspace.yaml

在根目录创建 pnpm-workspace.yaml,声明哪些目录是工作区包:

pnpm-workspace.yaml
packages:
  - 'apps/**'
  - 'packages/**'

2. 配置根目录 package.json

根目录的 package.json 通常只管理公共开发工具,不包含实际业务代码:

package.json
{
  "private": true,
  "scripts": {
    "dev": "pnpm -r run dev",
    "build": "pnpm -r run build"
  },
  "devDependencies": {
    "typescript": "^5.0.0"
  }
}

3. 为每个包设置 package.json

每个子包需要有自己的 package.jsonname 字段作为包名用于相互引用:

packages/ui/package.json
{
  "name": "@my-app/ui",
  "version": "1.0.0",
  "main": "./src/index.ts"
}

4. 在应用中引用本地包

在应用的 package.json 中,用 workspace:* 协议声明对本地包的依赖:

apps/web/package.json
{
  "name": "@my-app/web",
  "dependencies": {
    "@my-app/ui": "workspace:*",
    "@my-app/utils": "workspace:*"
  }
}

运行 pnpm install 后,pnpm 会自动将本地包通过符号链接安装,修改包源码后无需重新安装。

5. 配置 TypeScript 路径(可选)

如果使用 TypeScript,在根目录的 tsconfig.json 中配置 paths,让编辑器正确解析本地包的类型:

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@my-app/*": ["packages/*/src/index.ts"]
    }
  }
}

每个子包的 tsconfig.json 继承根配置:

apps/web/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "include": ["src"]
}

常用命令

命令说明
pnpm install安装所有包的依赖
pnpm -r run build在所有包中运行 build 脚本
pnpm --filter @my-app/web dev只在指定包中运行脚本
pnpm --filter @my-app/web add lodash只为指定包安装依赖
pnpm add -w typescript在根目录安装依赖(-w 表示 workspace root)

注意事项

幽灵依赖问题

pnpm 默认不提升依赖,子包只能访问自己 package.json 中声明的依赖。如果某个包 A 依赖了 B,而 B 依赖了 CA 不能直接导入 C,即使 C 已经安装在 node_modules 中。

遇到此类导入失败时,有两个解决方案:

方案一(推荐):在报错的包中显式声明缺失的依赖:

{
  "dependencies": {
    "c-package": "^1.0.0"
  }
}

方案二:在根目录创建 .npmrc 开启依赖提升(会丧失 pnpm 的隔离优势):

.npmrc
shamefully-hoist=true

参考 pnpm 官方文档 — workspaces

On this page