workspaces
主要是在仓库根目录建立虚拟依赖树,智能管理子包间的拓扑关系与共享依赖。
当项目演变为 Monorepo
结构时,传统依赖管理面临三重挑战:
- 依赖重复 :多个子项目共用的相同包时将在各自
node_modules
重复安装。 - 本地包引用复杂 :本地包(
packages/utils
)需手动npm link
或者发布到npm
中才能被其他本地包(packages/app
)使用。 - 批量操作低效 :为所有子包执行
npm install
耗时极长。
原生包管理工具支持的workspaces功能
npm
为了方便我们一般通过npm cli
提供的命令来创建子项目。通过npm init -h
查看具体使用规则。注意操作那个子包,需要确定好运行当前命令和当前子包根目录的路径关系。

创建多个包,在那个文件夹下创建,就需要参考哪个目录创建目录文件
js
npm init -w ./packages/a -w ./packages/b -y


如果修改子包
package.json
中的name
,需要在父包中运行命令来更新node_modules
中的本地包。然后可以手动删除之前映射的子包。
js
npm install -ws
对于
Monorepo
项目来说,如果想要在特定子包中安装第三方包,就需要指定子包的文件名。安装本地子包也是同样的命令。
js
// npm install 第三方包名/本地子包名 -w 本地包文件名
npm install lodash -w packages/a
npm install @zhang-glitch/b -w packages/a

运行子包命令
js
// npm run <command-name> -w 本地包文件名 // 运行单个子包命令
// npm run <command-name> -ws // 运行全部子包命令,如果某个子包无当前命令,则报错
npm run runIndexA -w packages/a
npm run runIndex -ws

发布包,需要运行以下命令。
js
// npm publish -ws // 发布所有包
// npm publish -w 包文件名 // 发布特定包
npm publish -w packages/a
如果发布出现下述问题,我们还需要再package.json
中配置
js
"publishConfig": {
"access": "public"
}



yarn
v1.x classic
- 实验性支持 :需在
package.json
中声明"workspaces"
字段启用。 - 依赖提升 :所有子包的依赖会提升到根目录的
node_modules
(hoisting)。 - 跨包链接 :自动处理
workspace:
协议依赖的软链接。
yarn
没有提供和npm
一样的cli来直接创建子包,所以我们需要手动创建配置。
bash
# 在父包目录下package.json中输入子包目录
{
"workspaces": ["packages/*"],
"private": true
}
# 创建子包
mkdir -p packages/a packages/b
# 初始化子包
cd packages/a && yarn init -y
# 安装本地包到node_modules
yarn install
# 查看创建的子包
yarn workspaces list

注意父项目必须是私有项目。

如果修改子包package.json
中的name
,需要在父包中运行命令来更新node_modules
中的本地包。。
bash
yarn install

如果我们想在本地包中使用另一个本地包,需要运行以下命令。注意workspace-name
都是子包名称,而不是子包目录,和npm
安装方式不一样。
bash
// yarn workspace <workspace-name> add <workspace-name>/第三方包
yarn workspace a add @zhang-glitch/b
yarn workspace @zhang-glitch/b add loadsh


运行命令子包命令
bash
# yarn workspaces run <command-name> // 运行所有子包命令,如果某个子包没有改命令则报错
yarn workspaces run runIndex
# yarn workspace <package-name> <command-name> // 运行单个子包命令
yarn workspace a runIndex


发布包,注意发布的包名必须唯一。
bash
# 原生未提供发布所有子包的命令
# yarn workspace 子包名称 publish // 发布特定包
yarn workspace @zhang-glitch/yarn-classic-a-demo publish
如果发布出现下述问题,我们还需要再package.json
中配置
js
"publishConfig": {
"access": "public"
}

发布成功

v2.x+ breey
如果是v1.x
版本,且Node.js >=16.10
,可以使用corepack prepare yarn@stable --activate
来更新全局yarn
版本,或者使用yarn set version berry
来升级yarn
版本。如果安装不成功可以通知系统代理。
js
# 设置 HTTP 代理
yarn config set proxy http://127.0.0.1:7890
# 设置 HTTPS 代理
yarn config set https-proxy http://127.0.0.1:7890

yarn
没有提供和npm
一样的cli来直接创建子包,所以我们需要手动创建配置。
bash
# 在父包目录下package.json中输入子包目录
{
"workspaces": ["packages/*"]
}
# 创建子包
mkdir -p packages/a packages/b
# 初始化子包
cd packages/a && yarn init -y
# 安装本地包到node_modules
yarn install
# 查看创建的子包
yarn workspaces list
如果运行
yarn install
报这个错误,表示当前目录不是workspaces
项目的根目录(即当前目录之前目录中也存在package.json,.yarnrc.yml
文件)
删除后即可运行成功,并在根目录生成
node_modlues
,将本地子包安装进去。 如果修改子包
package.json
中的name
,需要在父包中运行命令来更新node_modules
中的本地包。。
bash
yarn install

如果想要在特定子包中安装第三方包,就需要指定子包包名。安装本地子包也是同样的命令。
bash
yarn workspace 本地子包名 add 另一个本地包名/三方包
yarn workspace a add @zhang-glitch/yarn-berry-b-demo@workspace:*
yarn workspace a add lodash


如果按照上面的步骤执行后,再运行yarn workspace a add @zhang-glitch/berry-b-demo@workspace:*
是不成功的,具体解决方式可以看这里。即删除子包中的yarn.lock
文件即可链接成功。


运行命令子包命令
bash
# yarn workspaces foreach --all -p run <command-name> // 运行所有子包命令,如果某个子包没有该命令不会报错
yarn workspaces foreach --all -p run runIndex
# yarn workspace <package-name> <command-name> // 运行单个子包命令
yarn workspace a runIndex


发布包,注意发布的包名必须唯一。
bash
# 发布所有子包的命令
yarn workspaces foreach --all -p npm publish
# 发布特定包
# yarn workspace 子包名称 npm publish / yarn --cwd 包路径(packages/b) npm publish
yarn workspace @zhang-glitch/yarn-berry-b-demo npm publish
yarn --cwd packages/b npm publish
发布时,我们还需要在package.json
中配置
js
"publishConfig": {
"access": "public"
}
发布成功

pnpm
pnpm
没有提供和npm
一样的cli来直接创建子包,所以我们需要手动创建配置。
bash
# 在父包目录下pnpm-workspace.yaml中输入子包目录
packages:
- 'packages/*' # 匹配所有子包
# 创建子包
mkdir -p packages/a packages/b
# 初始化子包
cd packages/a && pnpm init
# 安装本地包到node_modules
pnpm install
如果修改子包package.json
中的name
,需要在父包中运行命令来更新node_modules
中的本地包。。
bash
pnpm install
如果想要在特定子包中安装第三方包,就需要指定子包包名。安装本地子包也是同样的命令。
bash
# pnpm add 另一个本地包名/三方包 --filter 包名
pnpm add lodash --filter a
pnpm add axios --filter @zhang-glitch/pnpm-b-demo
pnpm add @zhang-glitch/pnpm-b-demo@workspace:* --filter a


运行命令子包命令
bash
# pnpm run --recursive <command-name> // 运行所有子包命令,如果某个子包没有该命令不会报错
pnpm run --recursive runIndex
# pnpm run --filter 包名 <command-name> // 运行单个子包命令
pnpm run --filter a runIndex


发布包,注意发布的包名必须唯一。
bash
# 发布所有子包的命令
# pnpm publish --recursive/-r
# 发布特定包
# pnpm publish --filter 子包名
yarn workspace @zhang-glitch/yarn-classic-a-demo publish
如果发布出现下述问题,我们还需要再
package.json
中配置
js
"publishConfig": {
"access": "public"
}

发布成功

npm, yarn, pnpm workspaces区别
特性 | npm | Yarn (v1/Classic) | Yarn Berry (v2+) | pnpm |
---|---|---|---|---|
配置文件 | package.json |
package.json |
package.json + .yarnrc.yml |
pnpm-workspace.yaml |
依赖提升 | ✅ (根目录 hoisting) | ✅ (智能 hoisting) | ❌ (默认 PnP 无 node_modules ) |
❌ (隔离依赖) |
跨包依赖链接 | file: 协议 |
workspace: 协议 |
workspace: 协议 |
workspace: 协议 |
安装性能 | 较慢 | 快 (v1) / 极快 (Berry) | 极快 (零安装) | 最快 (硬链接复用) |
磁盘占用 | 高 | 中等 | 最低 (PnP) | 低 (全局存储复用) |
依赖隔离 | ❌ (易幽灵依赖) | ⚠️ (可配置) | ✅ (PnP 严格解析) | ✅ (默认隔离) |
Monorepo 工具链 | 需配合 Lerna | 内置 + Lerna 兼容 | 内置 + 插件扩展 | 内置 + 兼容 Lerna |
npm Workspaces
- 启用版本:npm ≥ v7.0
- 核心机制 :
- 顶层
package.json
中定义"workspaces"
字段(支持数组或packages/*
通配符)。 - 依赖提升到根级
node_modules
(hoisting),子包共享依赖。
- 顶层
- 特色 :
- 内置支持:无需额外工具,原生集成。
- 依赖安装 :
npm install
自动安装所有工作区依赖。 - 命令执行 :
npm run <command> -w <package>
或--workspaces
(全包执行)。
- 局限 :
- 依赖提升问题:可能引发版本冲突(不同子包需同一依赖的不同版本时)。
- 符号链接:子包通过符号链接(symlink)连接到根目录,但依赖解析仍以根目录为准。
- 性能:安装速度较慢,磁盘占用较高。
Yarn Workspaces
- 启用版本:Yarn v1(Classic)及 Yarn ≥ v2(Berry)
- 核心机制 :
- 类似 npm,根目录
package.json
中定义"workspaces"
。 - 依赖优化:更智能的 hoisting 策略,支持选择性提升。
- 类似 npm,根目录
- 特色 :
- 依赖协议 :支持
workspace:
协议(如"dependency": "workspace:package-a"
),直接链接本地包。 - 跨包依赖:自动处理子包之间的依赖关系,避免发布前手动构建。
- 高效安装:依赖去重和缓存机制优于 npm。
- Yarn Berry (v2+) :
- 零安装 :依赖缓存到
.yarn/cache
(可提交至 Git)。 - Plug'n'Play (PnP) :跳过
node_modules
,直接解析缓存依赖,提升速度和安全性。
- 零安装 :依赖缓存到
- 依赖协议 :支持
pnpm Workspaces
- 启用机制 :根目录
pnpm-workspace.yaml
定义包路径。 - 核心特色 :
- 内容可寻址存储 :依赖存储在全局
~/.pnpm-store
,硬链接到各项目,节省磁盘。 - 隔离性 :默认使用 symlink + 硬链接,子包依赖不提升到根目录,避免幽灵依赖。
- 严格模式 :子包只能访问显式声明的依赖(通过
.npmrc
设置node-linker=isolated
)。
- 内容可寻址存储 :依赖存储在全局
- 优势 :
- 安装速度最快:依赖复用率极高。
- 磁盘空间优化:相同依赖只存储一份。
- 依赖安全:杜绝隐式依赖访问。
往期年度总结
- 在上海的忙碌一年,依旧充满憧憬(2024)
- 四年沿海城市,刚毕业,一年3家公司
- 七月仿佛又回到了那一年(2023年中总结)
- 一位初入职场前端仔的年度终结 <回顾2022,展望2023>
- 大学两年半的前端学习
往期文章
专栏文章
🔥如果此文对你有帮助的话,欢迎💗关注 、👍点赞 、⭐收藏 、✍️评论, 支持一下博主~
公众号:全栈追逐者,不定期的更新内容,关注不错过哦!