小白理解workspace 协议

1 阅读3分钟

在使用 pnpm 的 monorepo 项目时,你可能见过这样的写法:

{
  "dependencies": {
    "@my-org/ui": "workspace:*"
  }
}

很多初学者都会疑惑:

  • workspace: 是什么?
  • 不写它行不行?
  • 它和版本号有什么关系?

这篇文章会用最直观、最少前置知识的方式,带你一次搞懂 pnpm workspace 协议的原理和使用


一、先说结论(一句话版本)

workspace: 协议的作用是:
强制 pnpm 使用当前 workspace 里的本地包,而不是去 npm 仓库下载。

它解决的不是“版本问题”,而是一个更基础的问题:

👉 这个依赖“从哪来”?


二、没有 workspace: 会发生什么?

先看一个最常见的 monorepo 结构:

packages/
  ui        (name: @my-org/ui)
apps/
  web

apps/web/package.json 中写:

{
  "dependencies": {
    "@my-org/ui": "^1.0.0"
  }
}

这时 pnpm 的行为是:

  1. 在 workspace 中查找 @my-org/ui
  2. 如果本地存在,并且版本满足 ^1.0.0
  3. 👉 默认使用本地包

也就是说:

即使你不写 workspace:,pnpm 也“通常”会用本地包

那问题来了——
既然这样,workspace: 有什么用?


三、workspace: 到底解决了什么问题?

1️⃣ 明确表达“这是一个本地依赖”

当你写:

{
  "@my-org/ui": "workspace:*"
}

你是在向 pnpm 明确声明一件事:

这个依赖必须来自当前 workspace,本地没有就直接报错。

它的意义不是“让 pnpm 能用本地包”,
而是 防止 pnpm 用错包


2️⃣ 防止“误用远程同名包”

假设有一天:

  • 你的 workspace 里还没有 @my-org/ui
  • 但 npm 仓库里刚好有一个同名包

如果你写的是:

"@my-org/ui": "^1.0.0"

pnpm 会:

👉 去 npm 仓库下载那个包

但如果你写的是:

"@my-org/ui": "workspace:*"

pnpm 会直接:

❌ 报错:workspace 中不存在该包

👉 这就是 workspace: 的安全价值


四、workspace 协议的“真实原理”(人话版)

pnpm 在安装依赖时,会先把每个依赖解析成一种“类型”:

  • 普通 semver(^1.0.0
  • workspace 协议
  • file / git / link 等

当 pnpm 看到:

"@my-org/ui": "workspace:*"

它内部会把这个依赖标记为:

“只能从 workspace Package Map 中解析”

接下来的规则非常简单:

  • ✅ workspace 里有 → 用本地包
  • ❌ workspace 里没有 → 直接失败
  • 🚫 不会查 npm registry

所以可以这样理解:

workspace: 是一种“来源锁定协议”


五、workspace:* / workspace:^ / workspace:~ 有什么区别?

这是很多人第一次看到会懵的地方。

结论先给出来:

它们在“安装阶段没有区别”,
区别只体现在“发布阶段”。


1️⃣ 安装阶段(最重要的阶段)

无论你写:

workspace:*
workspace:^
workspace:~

pnpm 都会:

  • 忽略版本判断
  • 直接使用本地包

👉 安装行为完全一致


2️⃣ 发布阶段(publish 到 npm 时)

workspace 协议 不会被发布,而是会被替换:

假设本地包版本是 1.2.3

写法发布后
workspace:*^1.2.3
workspace:^^1.2.3
workspace:~~1.2.3

所以:

workspace 后面的符号,
本质是在帮你决定将来别人怎么依赖你


六、什么时候“应该”用 workspace:?

✅ 推荐使用的场景

  • monorepo 内部包互相依赖
  • 希望明确表达“这是内部依赖”
  • 不希望误用远程同名包
  • 希望发布时自动生成合理版本号

👉 团队项目 / 长期维护项目:强烈推荐


❌ 可以不写的场景

  • 个人实验项目
  • 确定不会 publish
  • 对依赖来源不敏感

👉 即使不写,pnpm 多数时候也能正常工作
不写等于放弃了一层安全约束


七、一个完整示例

packages/ui/package.json

{
  "name": "@my-org/ui",
  "version": "1.0.0"
}

apps/web/package.json

{
  "dependencies": {
    "@my-org/ui": "workspace:*"
  }
}

pnpm 的理解是:

@my-org/ui 必须来自当前 workspace,
安装时用本地包,
发布时替换成真实版本号。


八、总结

workspace: 协议不是版本控制工具,而是依赖来源控制工具。
它的作用是明确告诉 pnpm:
“这个依赖只能来自当前 workspace,而不是外部仓库。”