# 名词解释
# monorepo
利用单一仓库来管理多个 packages 的一种策略,如早期的 lerna
# workspace
由上述单仓多包催生的管理方式,workspace(工作空间) 是 npm、yarn、pnpm 等包管理工具提供的一种特性,用于管理多个包的依赖关系。 合理配置 workspace 后,包之间互相依赖不需要使用 npm link,将在 install 时中处理
# 在 pnpm 中使用 workspace
A workspace must have a pnpm-workspace.yaml file in its root. A workspace also may have an .npmrc in its root. 如文档描述,启用 pnpm 的 workspace 需要在项目根目录创建 pnpm-workspace.yaml
# 项目结构
my-monorepo
├── docs
├── apps
│ └── web
├── packages
│ ├── ui
│ ├── eslint-config
│ └── shared-utils
├── pnpm-workspace.yaml
├── .npmrc => optional
└── sdk
2
3
4
5
6
7
8
9
10
11
# 根目录 package.json
private: If you set “private”: true in your package.json, then npm will refuse to publish it.
私有化为 true 时,在 publish 时 npm 将不会处理该 package,你可以在项目的根目录配置,或在不需要被 publish 的 workspace 中配置它
{
"name": "my-monorepo",
"private": true,
"script": {
"dev": "pnpm -r dev"
}
}
2
3
4
5
6
7
# pnpm-workspace.yaml
在该项目中指定位于 my-monorepo/apps/ 和 my-monorepo/packages/ 内的直接子目录为工作区如 web、ui 等 而 docs 本身则为一个工作区,则不需要通配符
packages:
- "docs"
- "apps/*"
- "packages/*"
2
3
4
# 具体语法 - glob 通配符
packages:
# 选择 packages 目录下的所有首层子目录的包
- 'packages/*'
# 选择 components 目录下所有层级的包
- 'components/**'
# 排除所有包含 test 的包
- '!**/test/**'
2
3
4
5
6
7
# 安装依赖
/my-monorepo/:
pnpm install
# 在根目录中安装依赖
/my-monorepo/:
pnpm add <package-name> -w
# or
pnpm add <package-name> --workspace-root
2
3
# 给指定 workspace(工作空间) 安装依赖
/my-monorepo/:
pnpm add <package-name> --filter <workspace-name>
# or
pnpm add lodash --filter docs
2
3
# 更新依赖
更新根目录依赖,看执行路径
pnpm update <package-name> [-w]
更新指定 workspace 依赖
pnpm update <package-name> --filter <workspace-name>
# or
pnpm update lodash --filter docs
2
3
卸载依赖
pnpm uninstall <package-name> [-w]
# or
pnpm uninstall <package-name> --filter <workspace-name>
# or
pnpm uninstall lodash --filter docs
2
3
4
5
# 执行脚本
执行 workspace 中的脚本
pnpm dev --filter docs
执行所有 workspace 中脚本
pnpm -r dev
# or
$ pnpm --recursive dev
2
3
或者在根目录的 package.json 中配置
/my-monorepo/package.json:
{
"name": "my-monorepo",
"script": {
"docs:dev": "pnpm dev --filter docs"
}
}
2
3
4
5
6
直接执行
pnpm docs:dev
# 安装内部 workspace 依赖
pnpm add <package-name> --filter <workspace-name>
# or
pnpm add web --filter docs
2
3
笔记
请注意你当前的 pnpm 版本,在 9.0 后 pnpm 修改 link-workspace-packages 的默认值为 false。该属性开启后,你在安装依赖时优先在本地链接,而不是从 registry(远程) 中下载。 所以在这个版本你若需要使用命令安装一个新的 workspace 中的依赖需要在 .npmrc 中启用 link-workspace-packages 当然主动在 package.json 中声明的依赖不受影响,如 web: "workspace:*",pnpm 还是会自动处理,这种不确定性的执行结果可能是导致 pnpm 在该版本中禁用了该值出处。
在 .npmrc 中
.npmrc:
link-workspace-packages = true
或临时启用
pnpm add <package-name> --filter <workspace-name> --link-workspace-packages=true
# or
pnpm add web --filter docs --link-workspace-packages=true
2
3
执行结果
/my-monorepo/packages/docs/package.json:
{
"name": "docs",
"dependencies": {
"web": "workspace:^"
}
}
2
3
4
5
6
# 什么是 workspace:^ - semver 版本
你可能好奇 workspace:^ 是怎么生成的,后面 pnpm 是怎么如何转化的?
当你将依赖项添加到 package.json 中时,pnpm 根据 .npmrc 或命令行中的 save-workspace-protocol 字段来决定是否使用 workspace: 协议,并根据 save-prefix 字段来决定版本的前缀(semver)
例如,save-prefix 为 "~" ,save-workspace-protocol 为 true 时
{
"name": "docs",
"dependencies": {
"web": "workspace:~1.0.0"
}
}
2
3
4
5
6
转化
{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}
2
3
4
5
6
7
8
{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}
2
3
4
5
6
7
8
# 总结
# 安装依赖
$ pnpm install
# 给指定 workspace 安装依赖
$ pnpm add <package-name> --filter <workspace-name>
# 卸载依赖
$ pnpm uninstall <package-name> --filter <workspace-name>
# 更新依赖
$ pnpm update <package-name> --filter <workspace-name>
# 给根目录安装依赖 - -w 为安装 -workspace-root
$ pnpm add <package-name> -<D>w
# 内部包的互相引用 - 前提 .npmrc 中配置 link-workspace-packages = true
# 若未配置需手动
$ pnpm add <package-name> --filter <workspace-name>
# 执行 workspace 中的脚本, 或者在根目录的 package.json 中配置
$ pnpm dev --filter docs
# 执行所有 workspace 中的脚本
$ pnpm -r dev
# or
$ pnpm --recursive dev
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25