在 Monorepo 中,依赖提升(dependency hoisting)并不会将每个子项目的依赖变成全局依赖,而是将相同版本的依赖提升到 Monorepo 根目录的 `node_modules` 中,以减少冗余的安装和磁盘空间占用。依赖提升的主要目的是优化依赖管理和安装速度,但每个子项目的依赖作用域仍然保持不变。
### 依赖提升的工作原理
1. **依赖提升**:相同版本的依赖会被提升到 Monorepo 根目录的 `node_modules` 中。
2. **符号链接**:子项目的 `node_modules` 中会创建符号链接,指向根目录的依赖。
3. **作用域保持**:每个子项目的依赖作用域仍然是局部的,即使依赖被提升到根目录,子项目只能访问其 `package.json` 中定义的依赖。
### 示例
假设你有一个 Monorepo 结构如下:
```
/monorepo
/packages
/project-a
package.json
/project-b
package.json
package.json
```
#### 根目录 `package.json`
```json
{
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"eslint": "^7.32.0"
}
}
```
#### `project-a` 的 `package.json`
```json
{
"name": "project-a",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21"
}
}
```
#### `project-b` 的 `package.json`
```json
{
"name": "project-b",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21",
"axios": "^0.21.1"
}
}
```
### 依赖提升后的结果
1. `lodash` 依赖会被提升到根目录的 `node_modules` 中,因为 `project-a` 和 `project-b` 都依赖于相同版本的 `lodash`。
2. `axios` 依赖会保留在 `project-b` 的 `node_modules` 中,因为只有 `project-b` 依赖它。
### 依赖作用域
- **根目录依赖**:根目录的 `devDependencies`(如 `eslint`)可以在整个 Monorepo 中使用,通常用于开发工具和共享依赖。
- **子项目依赖**:每个子项目的依赖(如 `lodash` 和 `axios`)仅在该子项目中使用,即使 `lodash` 被提升到根目录,`project-a` 和 `project-b` 仍然只能访问各自 `package.json` 中定义的依赖。
### 总结
依赖提升不会改变每个子项目的依赖作用域。每个子项目仍然只能访问其 `package.json` 中定义的依赖。依赖提升的目的是优化依赖管理和安装速度,而不是将所有依赖变成全局依赖。
如果 project-a 的 package.json 的 { "name": "project-a", "version": "1.0.0", "dependencies": { "lodash": "^4.17.21" } } 和 project-b 的 package.json { "name": "project-b", "version": "1.0.0", "dependencies": { "lodash": "^4.17.21", "axios": "^0.21.1" } } 后来 project-a 改为 { "name": "project-a", "version": "1.0.0", "dependencies": { "lodash": "^4.17.21", "axios": "^0.21.1" } } project-a 的node_modules的 axios 依赖会被删除,后提升到全局的node_modules?
如果你在 `project-a` 的 `package.json` 中添加了 `axios` 依赖,并且 `project-b` 也已经依赖了相同版本的 `axios`,那么在依赖提升的过程中,`axios` 依赖会被提升到 Monorepo 根目录的 `node_modules` 中。
### 具体步骤
1. **修改 `project-a` 的 `package.json`**:
```json
{
"name": "project-a",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21",
"axios": "^0.21.1"
}
}
```
2. **运行依赖安装命令**:
在 Monorepo 根目录下运行 `yarn install` 或 `lerna bootstrap`(如果使用 Lerna)。
3. **依赖提升**:
- Yarn 或 Lerna 会检测到 `project-a` 和 `project-b` 都依赖于相同版本的 `axios`。
- `axios` 依赖会被提升到 Monorepo 根目录的 `node_modules` 中。
- `project-a` 和 `project-b` 的 `node_modules` 中将不会单独安装 `axios`,而是通过符号链接指向根目录的 `axios` 依赖。
### 结果
- **根目录 `node_modules`**:
- `lodash` 和 `axios` 都会被安装在根目录的 `node_modules` 中。
- **`project-a` 和 `project-b` 的 `node_modules`**:
- `project-a` 和 `project-b` 的 `node_modules` 中不会单独安装 `axios`,而是通过符号链接指向根目录的 `axios` 依赖。
### 总结
通过依赖提升,`axios` 依赖会被提升到 Monorepo 根目录的 `node_modules` 中,从而减少冗余安装。`project-a` 和 `project-b` 的 `node_modules` 中将不会单独安装 `axios`,而是通过符号链接指向根目录的 `axios` 依赖。这样可以优化依赖管理和安装速度。
pnpm 支持依赖提升吗,类式yarn的功能
是的,pnpm 支持依赖提升(dependency hoisting),类似于 Yarn 的功能。pnpm 提供了一个名为 `pnpm-workspace.yaml` 的配置文件,用于管理 Monorepo 中的多个包,并且可以通过配置来启用依赖提升。
### 配置示例
1. **创建 `pnpm-workspace.yaml` 文件**:
在 Monorepo 根目录下创建一个 `pnpm-workspace.yaml` 文件,用于定义工作区。
```yaml
packages:
- 'packages/*'
```
2. **配置 `package.json` 文件**:
确保 Monorepo 根目录的 `package.json` 文件中包含以下内容:
```json
{
"private": true,
"devDependencies": {
"eslint": "^7.32.0"
}
}
```
3. **子项目的 `package.json` 文件**:
在每个子项目的 `package.json` 文件中定义各自的依赖。
例如,`project-a` 的 `package.json`:
```json
{
"name": "project-a",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21",
"axios": "^0.21.1"
}
}
```
`project-b` 的 `package.json`:
```json
{
"name": "project-b",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21",
"axios": "^0.21.1"
}
}
```
4. **运行 `pnpm install`**:
在 Monorepo 根目录下运行 `pnpm install`,pnpm 会自动处理依赖提升,并将相同版本的依赖提升到根目录的 `node_modules` 中。
### 依赖提升配置
pnpm 默认会进行依赖提升,但你也可以通过配置来控制提升行为。在 `pnpm-workspace.yaml` 文件中,可以使用 `hoist` 配置项来启用或禁用依赖提升。
```yaml
packages:
- 'packages/*'
hoist:
- '*'
```
### 总结
pnpm 支持依赖提升功能,类似于 Yarn 的 Workspaces。通过配置 `pnpm-workspace.yaml` 文件,你可以在 Monorepo 中高效地管理多个包,并减少冗余依赖的安装。pnpm 的依赖提升功能可以显著优化安装速度和磁盘空间使用。