解决的问题
Monorepo
管理方式解决了如下问题:
- 跨项目的改动困难:更改核心库需要在多个仓库中手动更新。
- 版本不一致:不同项目可能因为未及时更新依赖而导致的版本冲突。
- 流程复杂化:多个代码仓可能意味着重复配置多个 CI/CD 流程。
- 团队协作问题:不同的项目分属不同的仓库,增加了团队之间沟通的工作量。
初始化仓库
# Node 18+、pnpm 9+ 建议
mkdir my-monorepo && cd my-monorepo
git init
pnpm init -y
在根 package.json
补充(指定工作区包管理器、方便工具识别):
{
"name": "my-monorepo",
"private": true,
"packageManager": "pnpm@9.0.0",
"scripts": {
"dev": "pnpm -r --parallel dev",
"build": "pnpm -r build",
"lint": "pnpm -r lint",
"test": "pnpm -r test"
}
}
新建 .npmrc
(一些常用工作区优化):
; 在工作区根目录只维护一个 pnpm-lock.yaml(所有子包共享同一把锁),
; 方便 CI 和保证各包依赖版本一致。设为 false 时每个子包都会各自生成锁文件。
shared-workspace-lockfile = true
; 当依赖能在本地工作区里找到且版本满足时,用符号链接直接链接本地包,
; 而不是从 registry 下载,提升本地开发联调效率。
link-workspace-packages = true
; 当既能用本地工作区的包也能用远端版本时,优先使用本地工作区包。
;(若想模拟“真实用户安装远端包”的场景,可临时关掉)
prefer-workspace-packages = true
; 自动安装缺失的 peerDependencies,减少“缺少 peer 依赖”的报错。
; 注意:自动装的 peer 通常不会写入 package.json,想显式固定版本建议手动添加。
auto-install-peers = true
定义工作区
根目录新建 pnpm-workspace.yaml
:
packages:
- "apps/*"
- "packages/*"
跨项目依赖
my-monorepo/
pnpm-workspace.yaml
packages/
utils/
package.json
src/index.ts
apps/
web/
package.json
src/main.ts
pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
packages/utils/package.json
{
"name": "@acme/utils",
"version": "0.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsup src/index.ts --dts"
},
"devDependencies": {
"tsup": "^8.0.0",
"typescript": "^5.4.0"
}
}
packages/utils/src/index.ts
export const sum = (a: number, b: number) => a + b;
apps/web/package.json
{
"name": "web",
"private": true,
"type": "module",
"scripts": {
"dev": "node src/main.ts",
"build": "echo web build"
},
"dependencies": {
"@acme/utils": "workspace:*"
}
}
apps/web/src/main.ts
import { sum } from "@acme/utils";
console.log("sum:", sum(1, 2));
大概就是这样
加入 Turbo 做任务编排与缓存
官网: https://www.npmjs.com/package/turbo
pnpm -w add -D turbo
turbo.json:
{
"pipeline": {
"build": {
"outputs": ["dist/**", "build/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {}
}
}
根 package.json
脚本可改为:
{
"scripts": {
"dev": "turbo run dev --parallel",
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
}
}
版本管理与发包
如果 packages/*
要发布到 npm
:
pnpm dlx changeset init
pnpm changeset # 交互选择要发版的包与版本类型
pnpm changeset version # 写回 package.json
pnpm -r publish --access public
每个可发布包建议加上:
"publishConfig": { "access": "public" }
常见坑
- peerDependencies 未满足:在根或 app 安装 React 等 peer 依赖即可(
auto-install-peers=true
能减少报错)。 - 包互相引用循环或类型没刷:给库包加
tsup
/tsc -b
构建,app 依赖构建产物;或使用paths
+tsup --watch
。 - 路径解析问题:确保
moduleResolution: "Bundler"
(或NodeNext
)与工具一致;x
下通常没问题。 - 过滤器不好使:确认包名与
--filter
值一致(可用pnpm list -r
查看工作区包)。