章
目
录
常见的前端开发多项目管理方式有两种,分别是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
的结合使用,我们能够更高效地管理多项目,减少依赖管理的复杂性,提高开发效率。希望大家在实际项目中可以尝试运用这种方式。