章
目
录
常见的前端开发多项目管理方式有两种,分别是multirepo和monorepo 。今天,咱们就深入探讨一下这两种方式,重点讲讲如何结合pnpm来实现更高效的monorepo项目管理。
一、multirepo与monorepo介绍
(一)multirepo管理模式
multirepo这种管理方式,简单来说,就是把每个项目或者模块都单独放在各自的git仓库里。这就好比每个项目都是一个独立的“小房子”,各自住在不同的“地方”,需要我们分别去维护这些“小房子”。它的项目架构类似下面这样:
app/ # 项目A (git@github.com/app.git)
app2/ # 项目B (git@github.com/app2.git)
share/ # 公共模块 (git@github.com/share.git)
这种方式的好处是各个项目之间相互独立,互不干扰。但缺点也很明显,每个仓库都要单独维护,管理成本比较高,尤其是当公共模块有更新时,在多个项目中同步更新就会变得很麻烦。
(二)monorepo管理模式
monorepo则是另一种思路,它把多个项目或者模块都放在同一个仓库里。在这个“大仓库”里,不仅有公共的package.json ,每个项目还都有属于自己的package.json 。这就好比大家住在同一个“大院”里,每个人有自己的“小房间”,公共的东西放在“大院”的公共区域,个人的物品放在自己的“小房间”。
在依赖管理方面,对应公共的依赖可以安装到公共的node_modules里,而每个项目特有的依赖就安装到各自的node_modules里,这样就能实现依赖的共享。而且,各个子模块之间还能互相引用,不用非得发布成npm包,管理起来更加高效。monorepo的项目架构一般是这样的:
- monorepo/
- packages/
- app/ # 项目A
- app2/ # 项目B
- share/ # 公共模块
- package.json
二、pnpm + monorepo实战操作
接下来,咱们就动手实践一下,看看如何用pnpm和monorepo进行多项目管理。
(一)安装pnpm
首先,得在全局安装pnpm 。在命令行里输入下面这条命令就能完成安装:
npm install -g pnpm
这条命令会借助npm工具,在全局环境下安装pnpm ,安装好之后,我们就能在任何地方使用pnpm命令了。
(二)初始化monorepo项目
- 创建项目文件夹并初始化
package.json:先创建一个名为pnpm-monorepo的文件夹,这就是我们项目的“大本营”。然后进入这个文件夹,用pnpm init命令初始化一个package.json文件,它会记录项目的基本信息。
mkdir pnpm-monorepo
cd pnpm-monorepo
pnpm init
初始化后的package.json文件内容大致如下:
// package.json
{
"name": "pnpm-monorepo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- 创建子模块:接着,在
pnpm-monorepo文件夹下创建一个packages文件夹,用来存放各个子模块。这里我们创建两个子模块,分别是app和share。- 对于
app模块,我们用pnpm create vite app命令创建一个vue项目。这个命令会基于vite工具快速搭建一个vue项目的基础结构。
- 对于
cd packages
pnpm create vite app
创建完成后,app模块的package.json文件内容如下:
// packages/app/package.json
{
"name": "app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.2",
"vite": "^6.3.1"
}
}
- 对于`share`模块,我们用`pnpm init`初始化一个项目,然后新建一个`index.js`文件作为入口文件。在这个文件里,我们暴露一个公共方法,方便其他模块调用。比如:
// packages/share/index.js
function add(a, b) {
return a + b;
}
export default { add };
share模块的package.json文件内容如下:
// packages/share/package.json
{
"name": "share",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- 配置
workspace:在pnpm-monorepo文件夹下创建一个pnpm-workspace.yaml文件,用来配置子模块。在这个文件里,我们指定packages目录下所有模块都是monorepo的子模块,内容如下:
# pnpm-workspace.yaml
packages:
- 'packages/*' # 指定 packages 目录下所有模块都为 monorepo 的子模块
到这里,一个基本的monorepo项目架构就搭建好了,目录结构如下:
- pnpm-monorepo/
- packages/
- app/ # vue 项目
- share/ # 公共模块
- pnpm-workspace.yaml
- package.json
(三)依赖管理
- 安装公共依赖:公共依赖就是每个子模块都会用到的依赖。比如,我们的
vue项目和share模块都依赖lodash-es模块,就可以把它作为公共依赖安装到根目录的node_modules里。通过添加-w参数(-w是--workspace-root的简写),就能实现这一操作:
pnpm i lodash-es -w
- 安装子模块依赖:子模块自己特有的依赖,要安装到各自的
node_modules里。例如,只有vue项目用到了axios模块,那就可以把它安装到app项目的node_modules里。通过--filter <package-name>参数(--filter可以简写为-F,这里的package-name是package.json文件中的name字段),可以指定安装到哪个子模块:
pnpm i axios --filter app
- 模块共享:如果想在
vue项目(app模块)里引用share模块的add方法,就需要把share模块安装到app项目的node_modules里。可以通过--workspace参数,让pnpm去当前workspace查找相关模块,命令如下:
pnpm i share --filter app --workspace
执行完这个命令后,app项目的packages.json文件里就会多一个share模块的依赖,类似这样:
...
"dependencies": {
"share": "workspace:^",
"vue": "^3.5.13"
}
...
这里的workspace表示当前工作空间,^表示安装最新版本。当然,我们也可以直接在package.json的依赖里声明"share": "workspace:^" ,这样在安装时就不用再加--workspace参数了。如果想把share模块作为公共依赖安装,在后面再加一个-w参数就行:
pnpm i share --workspace -w
三、总结
最后,咱们来总结一下常用的pnpm命令:
pnpm i:这个命令可以安装项目里所有的依赖,包括公共依赖和子模块依赖。pnpm i -w:专门用来安装公共依赖到根目录的node_modules里。pnpm i --filter <package-name>:用于安装指定子模块的依赖到它自己的node_modules里。pnpm i <package-name1> --filter <package-name2> --workspace:把当前workspace中的某个模块安装到指定项目的node_modules里。pnpm i --workspace -w:将当前workspace中的模块安装到根目录的node_modules里,作为公共依赖。
通过pnpm和monorepo的结合使用,我们能够更高效地管理多项目,减少依赖管理的复杂性,提高开发效率。希望大家在实际项目中可以尝试运用这种方式。





