Monorepo 管理多个包
基于 Monorepo 管理多个包(子项目)能够提高开发和管理效率,尤其是当项目包含多个模块或服务时。Monorepo 可以通过 pnpm
、Yarn workspaces
等工具来实现多包管理,同时不同模块间可以共享代码。
下面我会详细讲解如何使用 pnpm
来配置一个简单的 Monorepo 项目,并演示如何在不同模块之间使用共享代码。
1. 初始化 Monorepo 项目
首先,我们通过 pnpm
创建一个 Monorepo 项目,并设置 pnpm-workspace.yaml
进行工作空间配置。
初始化项目
mkdir my-monorepo
cd my-monorepo
pnpm init -y
创建 pnpm-workspace.yaml
在项目根目录下创建 pnpm-workspace.yaml
,指定需要管理的工作空间路径:
packages:- 'packages/*'- 'apps/*'
这表示 pnpm
会管理 packages/
和 apps/
目录下的所有子项目。
2. 创建多个包
在 Monorepo 中,通常有多个模块(包),例如可以将共享的工具函数、组件等提取为独立的包供其他项目使用。我们现在创建两个包,一个是共享库 packages/utils
,另一个是应用项目 apps/app1
。
创建共享工具包
mkdir -p packages/utils
cd packages/utils
pnpm init -y
在 packages/utils
下创建一个工具函数文件:
// packages/utils/src/index.js
export function sayHello(name) {return `Hello, ${name}!`;
}
在 packages/utils/package.json
中确保添加以下配置,暴露入口文件:
{"name": "@my-org/utils","version": "1.0.0","main": "src/index.js"
}
创建应用包
mkdir -p apps/app1
cd apps/app1
pnpm init -y
在 apps/app1/package.json
中添加对共享包的依赖:
{"name": "app1","version": "1.0.0","dependencies": {"@my-org/utils": "workspace:*"}
}
"workspace:*"
表示这个依赖项来自当前 Monorepo 的工作空间中的 packages/utils
包。
3. 配置包的依赖与互相调用
在 apps/app1
中,我们可以直接使用 @my-org/utils
包提供的工具函数。
创建一个简单的应用入口:
// apps/app1/src/index.js
import { sayHello } from '@my-org/utils';console.log(sayHello('Monorepo'));
4. 安装依赖并运行项目
回到项目根目录,运行以下命令:
pnpm install
这会安装所有包的依赖项,并且会自动链接 Monorepo 内部的包。
运行 apps/app1
中的脚本:
cd apps/app1
node src/index.js
你会看到输出:
Hello, Monorepo!
5. 使用 Vite 构建应用
假设你想在 apps/app1
中创建一个基于 Vite 的 Vue 项目,并且使用 @my-org/utils
提供的工具包。
初始化 Vite 项目
在 apps/app1
中创建 Vite 项目:
cd apps/app1
pnpm create vite
选择 Vue 模板。接下来,安装依赖:
pnpm install
使用共享工具包
在 apps/app1/src/App.vue
中引入并使用工具函数:
<template><div>{{ greeting }}</div>
</template><script setup>
import { ref } from 'vue';
import { sayHello } from '@my-org/utils';const greeting = ref(sayHello('Vue with Monorepo'));
</script>
配置 vite.config.js
别名
为了能够让 Vite 正常识别并引入 @my-org/utils
包,可能需要在 vite.config.js
中配置路径别名:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';export default defineConfig({plugins: [vue()],resolve: {alias: {'@my-org/utils': path.resolve(__dirname, '../../packages/utils/src'),},},
});
6. Monorepo 中的测试与构建
使用 Vitest 测试工具包
你可以为 packages/utils
包添加单元测试。安装 Vitest:
cd packages/utils
pnpm add -D vitest
在 packages/utils/src/index.test.js
中编写测试:
import { describe, it, expect } from 'vitest';
import { sayHello } from './index';describe('sayHello', () => {it('returns greeting message', () => {expect(sayHello('Test')).toBe('Hello, Test!');});
});
运行测试:
pnpm vitest
构建整个 Monorepo 项目
你可以通过 pnpm
的 workspace 机制一次性构建所有子项目:
pnpm run build --filter "apps/*"
这种方式可以确保按依赖关系先构建 packages/utils
,然后构建 apps/app1
。
7. 工作流优化(Hoisting 和缓存)
Hoisting 共享依赖
pnpm
默认会将共享的依赖包提升到顶层,减少重复安装的依赖项。你可以通过设置 .npmrc
文件来控制 Hoisting:
hoist = true
依赖缓存
pnpm
利用全局缓存机制提高安装速度。所有包会缓存到全局存储区域,减少网络下载和重复安装。
总结
- Monorepo 基础配置:通过
pnpm-workspace.yaml
定义包管理路径。 - 模块共享与复用:在 Monorepo 中,使用内部依赖
"workspace:*"
来共享和引用模块。 - Vite 项目与共享模块集成:通过
vite.config.js
配置路径别名,使应用能够引入 Monorepo 内部的包。 - 测试与构建:通过
pnpm
一次性构建和测试所有子项目。
这样,Monorepo 可以有效地组织和管理多个模块或项目,适合大型项目开发场景。