使用 pnpm 创建 Monorepo 项目
什么是 Monorepo
Monorepo(Monolithic Repository,单体仓库)是一种代码管理的策略,与“多仓库”相对。它指的是将多个项目、包或应用程序存储在同一个版本控制仓库(通常是 Git)中。
简单来说,就是把原本分散在几十个甚至上百个独立仓库里的代码,全部放到同一个仓库里进行管理。
优点
- 代码共享与复用及其方便
如果 web-app 需要修改 utils 中的一个函数,你只需要在同一个仓库中修改,然后直接发布新版本即可。
不需要像多仓库那样,需要去 utils 仓库提 PR,等待合并,然后在 web-app 仓库升级版本,再拉取代码。
- 统一的依赖管理
所有项目使用相同的 package.json(或通过锁文件管理),避免了版本冲突。例如,不会出现 A 项目用 React 18,B 项目用 React 17 导致的不兼容问题。
安装依赖只需运行一次命令(如 npm install),大大节省磁盘空间和时间。
- 原子化提交
你可以在一次 Commit 中同时修改 UI 组件库和业务应用。这保证了代码的一致性,不会出现“库改了,应用还没适配”导致中间状态报错的情况。
- 统一的 CI/CD 和工具链
所有项目可以使用同一套 ESLint、Prettier、TypeScript 配置和构建脚本。
只需要维护一套 CI/CD 流程,可以针对被修改的文件进行增量构建,提高效率。
- 简化跨团队协作
团队成员可以更容易地查看和调试其他团队的代码,因为所有代码都在手边,不需要切换仓库。
使用 pnpm 创建 Monorepo 项目
安装 pnpm
bash
npm install -g pnpm初始化 Monorepo
bash
pnpm init根目录会有一个 package.json, 最重要的就是 name(后期会用)
里面
scripts里面写命令,pnpm -r run dev就是运行所有子项目的 dev 命令-r就是递归
json
{
"name": "basic",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"all:start": "pnpm -r run dev"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1"
}添加工作区
- 我这里就是举个例子,实际项目按照自己的需求来划分
bash
baisc
├── apps
│ ├── h5
│ └── web
├── packages
│ ├── command
│ ├── date
│ └── ui
├── pnpm-workspace.yaml
├── .gitignore
├── package.json
└── README.mdpnpm-workspace.yaml
- 这里就是规定了包, 他只会找到这些包,其他包不会找
yaml
packages:
- "packages/**"
- "apps/**"创建每个文件夹下面的 package.json
- packages/command/package.json
注意
- name 必须以 @开头,后面是 workspace 的 name,然后是包的 name,用 / 分隔
- scripts 里面写命令,
pnpm run dev就是运行当前项目的 dev 命令 - type: module 必须写,当成模块
代码如下
json
{
"name": "@basic/command",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module"
}- packages/date/package.json
注意
- name 必须以 @开头,后面是 workspace 的 name,然后是包的 name,用 / 分隔
- scripts 里面写命令,
pnpm run dev就是运行当前项目的 dev 命令 - type: module 必须写,当成模块
代码如下
json
{
"name": "@basic/date",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module"
}- packages/ui/package.json
注意
- name 必须以 @开头,后面是 workspace 的 name,然后是包的 name,用 / 分隔
- scripts 里面写命令,
pnpm run dev就是运行当前项目的 dev 命令 - type: module 必须写,当成模块
代码如下
json
{
"name": "@basic/ui",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module"
}为各个不同的文件夹写对应的方法
- packages/command/index.js
js
export function countChars(str) {
const result = {};
for (let i = 0; i < str.length; i++) {
const char = str[i];
if (result[char]) {
result[char]++;
} else {
result[char] = 1;
}
}
return Object.entries(result);
}
console.log(countChars("helloworld"));- packages/date/index.js
js
export function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
console.log(formatDate(new Date()));- packages/ui/index.js
js
export function toUpperCase(str) {
return str.toUpperCase();
}
console.log(toUpperCase("adadada"));到根目录
- 运行命令
bash
npm run all:start结果
- 控制台要是没报错,并且打印出结果.则代表成功
apps 文件夹
创建每个文件夹下面的 package.json
- apps/h5/package.json
注意
- name 必须以 @开头,后面是 workspace 的 name,然后是包的 name,用 / 分隔
- scripts 里面写命令,
pnpm run dev就是运行当前项目的 dev 命令 - type: module 必须写,当成模块
- dependencies 里面写依赖,
@basic/command就是依赖 workspace 的 command 包
代码如下
json
{
"name": "@basic/h5",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module",
"dependencies": {
"@basic/command": "workspace:*",
"@basic/date": "workspace:*",
"@basic/ui": "workspace:*"
}
}- apps/web/package.json
注意
- name 必须以 @开头,后面是 workspace 的 name,然后是包的 name,用 / 分隔
- scripts 里面写命令,
pnpm run dev就是运行当前项目的 dev 命令 - type: module 必须写,当成模块
- dependencies 里面写依赖,
@basic/command就是依赖 workspace 的 command 包
代码如下
json
{
"name": "@basic/web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module",
"dependencies": {
"@basic/command": "workspace:*",
"@basic/date": "workspace:*",
"@basic/ui": "workspace:*"
}
}安装依赖
- 运行命令
bash
pnpm install为各个不同的文件夹写对应的方法
- apps/h5/index.js
js
import { countChars } from "@basic/command";
import { formatDate } from "@basic/date";
import { toUpperCase } from "@basic/ui";
console.log(toUpperCase("testone"));
console.log(formatDate(new Date()));
console.log(countChars("goodafternoon"));- apps/web/index.js
ts
import { countChars } from "@basic/command";
import { formatDate } from "@basic/date";
import { toUpperCase } from "@basic/ui";
console.log(toUpperCase("testone"));
console.log(formatDate(new Date()));
console.log(countChars("goodafternoon"));到根目录
- 运行命令
bash
pnpm run all:start结果
- 控制台要是没报错,并且打印出结果.则代表成功
总结
- 这样一个基础版本的 Monorepo 就完成了