monorepo项目的搭建
1.全局安装pnpm
npm i pnpm -g
2.使用pnpm初始化项目
pnpm init
得到一个初始化的package.json的文件,内容如下:
{
"name": "monorepo",
"version": "1.0.0",
"description": "monorepo测试项目",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"private": true,
"packageManager": "pnpm@10.10.0"
}
3.目录创建和pnpm-workspace.yaml配置
3.1 创建3个vue3项目
在package.json文件所在目录下执行如下命令,然后根据需要选择对应特性和输入项目名称:
pnpm create vue@latest
使用以上命令创建了vue3_manager、vue3_bigShow、vue3_main三个项目
3.2 使用vite创建一个vue2项目和一个react项目
在package.json文件所在目录下执行如下命令,然后根据需要选择对应特性和输入项目名称:
pnpm create vite
分别创建了vue2_ui和react_test两个项目
目前的文件夹目录如下:
|-- package.json
|-- pnpm-workspace.yaml
|-- vue3_main
|-- web
|-- react_test
|-- vue2_ui
|-- vue3_bigShow
|-- vue3_manager
小技巧:可以使用
pnpm add -g mddir安装mddir,然后在项目所在目录中执行mddir就会输出一个当前项目目录结构的markdown文件列表清单directoryList.md,省去自己拼装目录结构的麻烦。
3.3 配置workspace.yaml文件
配置如下:
packages:
#主项目
- 'vue3_main'
#其他所有子项目
- 'web/*'
#通用组件库
- 'components'
#通用工具库
- 'utils'
# 定义依赖项和版本号
catalog:
# 可以通过 "catalog:" 引用
prettier: 3.5.3
vite: ^6.2.4
eslint-plugin-vue: ~10.0.0
eslint: ^9.22.0
typescript: ~5.8.0
catalogs:
# 可以通过 "catalogs:vue3" 引用
vue3:
vue: ^3.5.13
vue-router: ^4.5.0
pinia: ^3.0.1
vue2:
vue: ~2.7.14
"@vitejs/plugin-vue2": ^2.3.3
注意:因为依赖包@vitejs/plugin-vue2有@这个特殊字符需要使用双引号括起来在安装的时候才不会报错。
3.4 catalog配置项知识补充
catalog是工作空间的一个功能,将依赖的项目版本作为可复用变量。在实际应用中如下:
// package.json文件中
{
"name": "vue3-bigshow",
"devDependencies": {
"prettier": "catalog:",// 这里将替换为: "prettier": "3.5.3"
"typescript": "catalog:",// 这里将替换为: "typescript": "~5.8.0"
"vite": "catalog:",// 这里将替换为: "vite": "^6.2.4"
}
}
使用catalog就是使用的默认目录不能重命名只能和package.json中依赖项目一样
// package.json文件中
{
"name": "vue3-bigshow",
"dependencies": {
"pinia": "catalogs:vue3",// 这里将替换为: "pinia": "^3.0.1"
"vue": "catalogs:vue3", // 这里将替换为: "vue": "^3.5.13"
"vue-router": "catalogs:vue3" // 这里将替换为: "vue-router": "^4.5.0"
}
}
使用catalogs可以创建任意命名的路由,比如区分vue2和vue2依赖的vue和vue-router包的版本。
总结:在工作空间中,许多包使用相同的依赖项是很常见的。 在编写 package.json 文件时,目录可以减少重复,并在此过程中提供一些好处:
- 维护唯一版本 - 我们通常希望在工作空间中共同的依赖项版本一致。 Catalog 让工作区内共同依赖项的版本更容易维护。 重复的依赖关系可能会在运行时冲突并导致错误。 当使用打包器时,不同版本的重复依赖项也会增大项目体积。
- 易于更新 — 升级或者更新依赖项版本时,只需编辑
pnpm-workspace.yaml中的目录,而不需要更改所有用到该依赖项的package.json文件。 这样可以节省时间 — 只需更改一行,而不是多行。 - 减少合并冲突 — 由于在升级依赖项时不需要编辑
package.json文件,所以这些依赖项版本更新时就不会发生 git 冲突。
4.项目启动脚本的添加
4.1 指定安装包工具只能是pnpm
在package.json中添加如下命令:
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
npm的生命周期钩子函数
preinstall就会在安装之前执行检测当前安装的脚本是否是pnpm
4.2 在monorepo项目的根目录的package.json中添加命令以运行对应项目:
{
"scripts": {
"preinstall": "npx only-allow pnpm",
"vue3_main:dev":"pnpm -F vue3_main run dev",
"vue3_manager:dev":"pnpm -F vue3_manager run dev",
"vue3_bigShow:dev":"pnpm -F vue3_bigShow run dev",
"vue2_ui:dev":"pnpm -F vue2_ui run dev",
"react_test:dev":"pnpm -F react_test run dev"
}
}
其中
-F是命令—filter的缩写,用于筛选工作区中的特定项目
5.代码复用
5.1 工具函数复用
- 文件创建和方法的添加
在utils项目文件下执行pnpm init初始化项目,然后修改package.json文件如下:
{
"name": "utils",
"version": "1.0.0",
"description": "",
"main": "./src/index.js",// 库项目入口
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.10.0"
}
最终utils项目目录结构如下:
|-- utils
|-- package.json
|-- src
|-- index.js
|-- utils
|-- com-func.js
其中utils/src/index.js是统一在这里导出函数方法的出口。
//utils/src/index.js
import * as comFunc from './utils/com-func.js'
export {comFunc}
其中utils/src/utils/com-func.js是具体的函数方法如下:
export function add(a,b){
return a+b
}
- uitils项目的共享操作方法
首先在monorepo/vue3_main/package.json/package.json下修改文件如下:
{
"name": "vue3_main",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
+ "utils": "workspace:*", // 主要是这一行添加通过workspace:协议关联本地的包,*表示匹配最新版本
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.8.3",
"vite": "^6.3.5",
"vue-tsc": "^2.2.8"
}
}
然后执行pnpm -F vue3_main add utils这样就在vue3_main项目下关联了utils包:
可以看到在vue3_main项目的node_modules文件下多出了utils包,里面的方法也正是刚才添加的方法。
在vue3_main项目中使用
// vue3_main/src/App.vue
<script setup lang="ts">
import {comFunc} from 'utils'// 这里直接引用
</script>
<template>
<div>
{{comFunc.add(1,2)}}
</div>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
启动项目pnpm run vue3_main:dev可以看到项目启动成功,并且页面显示的3
utils项目在vue3_main项目中是硬链接
当在utils的com-func.js中添加新的方法,vue3_main项目的node_modules/utils/src/com-func.js也会增加相应的方法,简单来说硬链接就是同一份文件,会和源文件同步增删改;这样既节省空间又能保持文件同步。
5.2 组件的复用
1.在components中创建项目和修改package.json
执行pnpm init初始化项目,然后修改package.json文件如下:
{
"name": "components",
"version": "1.0.0",
"description": "",
"main": "./src/index.ts",
"exports":{
".":"./src/index.ts"
},
"keywords": [],
"author": "",
"packageManager": "pnpm@10.10.0"
}
最终components目录结构如下:
|-- components
|-- package.json
|-- src
|-- index.ts
|-- components
|-- t-button
|-- index.vue
其中components/src/index.ts文件如下:
import Button from "./components/t-button/index.vue";
export {Button};
components/src/components/t-button/index.vue文件如下:
<template>
<button class="com-color">这是按钮组件123</button>
</template>
<script setup lang="ts"></script>
<style scoped>
.com-color {
color: red;
}
</style>
在vue3_main项目中添加对用的包依赖:
{
"dependencies": {
"components": "workspace:*"
}
}
执行pnpm add components -F vue3_main关联相关组件库,直接在vue3_main/src/app.vue文件中使用共享组件
<script setup lang="ts">
import {Button} from 'components'
</script>
<template>
<Button></Button>
<RouterView />
</template>
结果如下:
6.总结
monorepo项目有以下好处:
- 解决了多个项目代码复用问题,如
vue3_main使用components项目,不用创建私有库导致的各种配置问题。 - 解决了统一vue版本,统一typecript和eslint等包的版本问题,通过catalog控制。