pnpm + monorepo: 高效的项目管理方式
前言
在进行多项目管理时,一般有两种方式,multirepo
和 monorepo
。
multirepo
multirepo
是将每个项目或者模块分别放在各自的 git
仓库里,需要对每个仓库单独维护。
项目架构如下:
zsh
app/ # 项目A ([email protected]/app.git)
app2/ # 项目B ([email protected]/app2.git)
share/ # 公共模块 ([email protected]/share.git)
monorepo
monorepo
是将多个项目或者模块放在同一个仓库里,除了有公共的 packages.json
之外,每个项目都有自己的 packages.json
。
对应公共的依赖可以安装到公共的 node_modules
里,而每个项目的依赖则安装到各自的 node_modules
里,实现依赖共享
同时,每个子模块之间可以实现互相引用,而不需要发布成 npm
包,管理更加高效。
项目架构如下:
zsh
- monorepo/
- packages/
- app/ # 项目A
- app2/ # 项目B
- share/ # 公共模块
- package.json
pnpm + monorepo
接下来讲一下如何使用 pnpm
+ monorepo
来进行多项目管理。
安装 pnpm
首先,全局安装 pnpm
。
zsh
npm install -g pnpm
初始化 monorepo
创建一个 pnpm-monorepo
文件夹,然后通过 pnpm init
创建 package.json
。
zsh
mkdir pnpm-monorepo
cd pnpm-monorepo
pnpm init
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"
}
然后,创建一个 packages
文件夹,用来存放子模块。
我们在 packages
文件夹下创建两个子模块:app
和 share
。
对于app
模块,我们用 pnpm create vite app
创建一个 vue
项目。
zsh
cd packages
pnpm create vite app
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
文件。作为入口文件,在里面暴露一个公共方法:
javascript
// packages/share/index.js
function add(a, b) {
return a + b;
}
export default { add };
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
文件,配置子模块。
yaml
# pnpm-workspace.yaml
packages:
- 'packages/*' # 指定 packages 目录下所有模块都为 monorepo 的子模块
这个时候一个基本的 monorepo
项目架构就创建好了:
zsh
- pnpm-monorepo/
- packages/
- app/ # vue 项目
- share/ # 公共模块
- pnpm-workspace.yaml
- package.json
依赖管理
如果在根目录下 执行 pnpm install
,会自动安装根目录和所有子包的依赖项。
如果我们想分别安装依赖,可以通过指定参数来控制。
安装公共依赖
公共依赖是每个子模块都会用到的依赖,我们可以安装到公共的 node_modules
里。
比如,我们的 vue
项目和 share
模块都依赖了 lodash-es
模块,那么就可以作为公共依赖安装。
通过添加 -w
参数,可以安装到根目录的node_modules
里。
-w
是 --workspace-root
的简写。
zsh
pnpm i lodash-es -w
安装子模块依赖
子模块自己用到的依赖可以安装到各自的 node_modules
里。
比如,只有 vue
项目里用到了 axios
模块,那么就可以安装到 app
项目的 node_modules
里。
通过 --filter <package-name>
参数,可以指定安装到哪个子模块的 node_modules
里。
--filter
可以简写为 -F
。
注意,这里的 package-name
是 package.json
文件中的 name
字段。
zsh
pnpm i axios --filter app
模块共享
如果我想在 vue
项目里引用 share
模块的 add
方法,需要将 share
模块安装到 app
项目的 node_modules
里。
可以通过 --workspace
参数,告诉 pnpm
去当前 workspace
查找相关模块。
zsh
pnpm i share --filter app --workspace
这时 app
项目的 packages.json
文件就会多一个 share
模块的依赖。
json
...
"dependencies": {
"share": "workspace:^",
"vue": "^3.5.13"
}
...
其中,workspace
表示当前工作空间,^
表示安装最新版本。
也可以通过在 package.json
的依赖里声明 "share": "workspace:^"
,也可以指定具体的版本号。这样在安装时就不需要添加 --workspace
参数了。
如果想把 share
模块作为公共依赖安装的话,在后面再加一个 -w
参数就可以了。
zsh
pnpm i share --workspace -w
总结
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
里。