常见的前端开发多项目管理方式有两种,分别是multirepomonorepo 。今天,咱们就深入探讨一下这两种方式,重点讲讲如何结合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实战操作

接下来,咱们就动手实践一下,看看如何用pnpmmonorepo进行多项目管理。

(一)安装pnpm

首先,得在全局安装pnpm 。在命令行里输入下面这条命令就能完成安装:

npm install -g pnpm 

这条命令会借助npm工具,在全局环境下安装pnpm ,安装好之后,我们就能在任何地方使用pnpm命令了。

(二)初始化monorepo项目

  1. 创建项目文件夹并初始化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" } 
  1. 创建子模块:接着,在pnpm-monorepo文件夹下创建一个packages文件夹,用来存放各个子模块。这里我们创建两个子模块,分别是appshare
    • 对于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" } 
  1. 配置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 

(三)依赖管理

  1. 安装公共依赖:公共依赖就是每个子模块都会用到的依赖。比如,我们的vue项目和share模块都依赖lodash-es模块,就可以把它作为公共依赖安装到根目录的node_modules里。通过添加-w参数(-w--workspace-root的简写),就能实现这一操作:
pnpm i lodash-es -w 
  1. 安装子模块依赖:子模块自己特有的依赖,要安装到各自的node_modules里。例如,只有vue项目用到了axios模块,那就可以把它安装到app项目的node_modules里。通过--filter <package-name>参数(--filter可以简写为-F ,这里的package-namepackage.json文件中的name字段),可以指定安装到哪个子模块:
pnpm i axios --filter app 
  1. 模块共享:如果想在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里,作为公共依赖。

通过pnpmmonorepo的结合使用,我们能够更高效地管理多项目,减少依赖管理的复杂性,提高开发效率。希望大家在实际项目中可以尝试运用这种方式。