一、最核心的问题先回答
为什么我只在
pnpm-workspace.yaml里写了apps/*,
pnpm 就能知道apps/web、apps/api这些包,并把它们用起来?
答案只有一句话:
因为 pnpm 在启动时,
先用 Glob 规则找目录 → 再识别哪些是真正的包 → 在内存里记住它们的 name 和路径。
二、什么是 Glob?(一定要先懂这个)
1️⃣ Glob 是什么
Glob 是一种“用模式匹配文件路径”的规则
你每天其实都在用,比如:
ls *.js
ls apps/*
这里的 * 就是 Glob。
2️⃣ Glob 的“全称”是啥?
- 没有严格官方全称
- 约定俗成理解为:Global Pattern Matching
- 起源于 Unix Shell
👉 它不是 pnpm 发明的,是整个操作系统层面的东西。
3️⃣ Glob 能干什么?
Glob 只做一件事:
👉 在磁盘上找出“路径长得像”的文件或目录
⚠️ 重要:
- Glob 不懂什么是包
- Glob 不看 package.json
- 它只认路径形状
三、apps/* 到底是什么意思?
packages:
- apps/*
这句话的真实含义是:
“请在项目根目录下,
找出所有路径形状像apps/某个名字的目录。”
比如磁盘上有:
apps/web
apps/api
apps/docs
Glob 匹配结果就是这三个。
四、pnpm 是怎么一步步工作的?(重点)
第一步:pnpm 判断是不是 Workspace
pnpm 启动时先看:
有没有
pnpm-workspace.yaml?
- 有 → Workspace 模式
- 没有 → 单包模式
⚠️ pnpm 只认这个文件。
第二步:Glob 展开(找目录)
pnpm 读取:
packages:
- apps/*
然后:
- 使用 Glob 规则
- 遍历文件系统
- 找到所有匹配的目录:
apps/web
apps/api
apps/docs
⚠️ 此时 pnpm 还不知道谁是包。
第三步:识别“真正的包”
pnpm 接下来会逐个检查:
- apps/web → 有
package.json✅ - apps/api → 有
package.json✅ - apps/docs → 没有 ❌
只有有 package.json 的目录才算 workspace 包。
第四步:建立 Package Map(最关键)
pnpm 会读取每个包的 package.json 里的:
{
"name": "@my-org/web",
"version": "1.0.0"
}
然后在内存中建立一张表:
@my-org/web -> apps/web
@my-org/api -> apps/api
@my-org/ui -> packages/ui
👉 这一步非常重要:
- pnpm 认的是 name
- 不是目录名
- 不是路径
五、pnpm 是什么时候把包“连起来”的?
❌ 不是扫描时
✅ 是安装依赖时
比如 apps/web/package.json:
{
"dependencies": {
"@my-org/ui": "^1.0.0"
}
}
pnpm 在安装时会想:
- 我要找
@my-org/ui - Workspace 里有没有同名包?
- 有 → 用本地的
- 没有 → 去 npm 仓库下载
👉 所谓“连起来”,本质是:
把依赖指向本地 workspace 包,而不是远程包
六、如果没有 pnpm-workspace.yaml 会怎样?
pnpm 会:
- ❌ 不扫描其他目录
- ❌ 不建立包映射表
- ❌ 不知道本地还有同名包
结果就是:
即使你本地有
packages/ui,
pnpm 也会去 npm 仓库下载一个同名包。
七、mac 上怎么自己“看到” Glob 在干嘛?
macOS 默认用的是 zsh,它天生支持 Glob。
你可以直接在终端试:
# 看 Glob 匹配了哪些目录
ls apps/*
再试:
# 看哪些是真正的包
ls apps/*/package.json
👉 这两步,和 pnpm 内部做的事情几乎一模一样。
八、最容易踩的坑(小白必看)
❌ 错误写法
packages:
- apps
只会尝试:
apps/package.json
✅ 正确写法
packages:
- apps/*
九、终极一句话总结(背下来就够了)
Glob 只是用来找目录的规则。
pnpm 用 Glob 找到目录后,再通过 package.json 判断哪些是包,
并把它们的 name 和路径记在内存里。
真正把包“连起来”的,是后面的依赖解析,而不是 Glob 本身。
pnpm Workspace 全流程图
┌────────────────────────────┐
│ 你运行 pnpm install │
└─────────────┬──────────────┘
│
▼
┌────────────────────────────┐
│ ① 是否存在 pnpm-workspace │
│ .yaml ? │
└─────────────┬──────────────┘
有 │ 没有
▼ │ ▼
┌──────────────────┐ ┌──────────────────┐
│ Workspace 模式 │ │ 单包模式 │
└────────┬─────────┘ │(不扫描别的包) │
│ └──────────────────┘
▼
┌────────────────────────────┐
│ ② 读取 pnpm-workspace.yaml │
│ packages: │
│ - apps/* │
│ - packages/* │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ ③ Glob 展开(找目录) │
│ apps/* → │
│ apps/web │
│ apps/api │
│ apps/docs │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ ④ 判断是否是包 │
│ 有没有 package.json ? │
│ web ✅ │
│ api ✅ │
│ docs ❌ │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ ⑤ 读取 package.json │
│ name / version │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ ⑥ 建立 Package Map(内存) │
│ @my-org/web → apps/web │
│ @my-org/api → apps/api │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ ⑦ 依赖解析(install 阶段) │
│ web 依赖 @my-org/ui ? │
│ → workspace 里有吗? │
│ → 有 → 用本地包 │
│ → 没有 → 去 npm 仓库 │
└────────────────────────────┘