学习笔记二:monorepo项目的搭建

1,863 阅读6分钟

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_managervue3_bigShowvue3_main三个项目

3.2 使用vite创建一个vue2项目和一个react项目

package.json文件所在目录下执行如下命令,然后根据需要选择对应特性和输入项目名称:

pnpm create vite

分别创建了vue2_uireact_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 工具函数复用
  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
}
  1. 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包:

5036c9f06ebaea256038f94549e1281.jpg

可以看到在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项目中是硬链接
当在utilscom-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>

结果如下:

image.png

6.总结

monorepo项目有以下好处:

  1. 解决了多个项目代码复用问题,如vue3_main使用components项目,不用创建私有库导致的各种配置问题。
  2. 解决了统一vue版本,统一typecript和eslint等包的版本问题,通过catalog控制。