vue3 + pnpm 打造一個 monorepo 專案

柏成發表於2024-12-02

Monorepo 和 Multirepo

單一倉庫(Monorepo)架構,可以理解為:利用單一倉庫來管理多個packages的一種策略或手段;與其相對的是多倉庫(Multirepo)架構

Monorepo 目錄中除了會有公共的package.json依賴以外,在每個sub-package子包下面,也會有其特有的package.json依賴。

兄弟模組之間可以透過模組 package.json 定義的 name 相互引用,保證模組之間的獨立性

# monorepo目錄結構
monorepo-demo
├── packages
│   ├─ module-a
│   │  ├─ src             # 模組 a 的原始碼
│   │  ├─ node_modules    # 模組 a 的 node_modules
│   │  └─ package.json    # 僅模組 a 的依賴
│   └─ module-b
│      ├─ src             # 模組 b 的原始碼
│      └─ package.json    # 僅模組 b 的依賴
├── .eslintrc             # 配置檔案,對整個專案生效
├── node_modules          # 所有子包公共的 node_modules
└── package.json          # 所有子包公共的依賴

Multirepo 更傾向與在專案制中,將一個個專案使用不同的倉庫進行隔離,每一個專案下使用獨有的package.json來管理依賴

# multirepo-a目錄結構
multirepo-a
├── src
├── .eslintrc                
├── node_modules             
└── package.json   

# multirepo-b目錄結構
multirepo-b
├── src
├── .eslintrc                
├── node_modules             
└── package.json  

Monorepo 工具

在採用 Monorepo(單一倉庫)架構的軟體開發中,工具的選擇是至關重要的。合適的 Monorepo 工具能夠幫助團隊更高效地管理大規模程式碼庫、提升協同開發體驗以及最佳化構建和部署流程。

直至 2024 年,目前在前端界比較流行的 Monorepo 工具有 Pnpm WorkspacesYarn Workspacesnpm WorkspacesRush
TurborepoLernaYalc、和 Nx

強烈推薦使用Pnpm Workspaces 作為 Monorepo 專案的依賴管理工具😍😍😍

那麼 Monorepo 與包管理工具(npm、yarn、pnpm)之間是一種怎樣的關係?

這些包管理工具與 monorepo 的關係在於,它們可以為 monorepo 提供依賴安裝與依賴管理的支援,藉助自身對 workspace 的支援,允許在 monorepo 中的不同子專案之間共享依賴項,並提供一種管理這些共享依賴項的方式,這可以簡化依賴項管理和構建過程,並提高開發效率。

Monorepo 專案搭建

背景

傳統的多倉庫 Multirepo 模式,通常都是一個倉庫存放一個專案。比如現在你有三個專案,就需要建立三個遠端倉庫,並且需要為每個專案單獨安裝和升級依賴

而單一倉庫 Monorepo 模式,就是在一個倉庫中管理多個專案,這些專案可以是獨立的,也可以相互依賴。透過 Monorepo,多個專案可以共享依賴。比如多個專案都需要 lodash,那我們也只需安裝一次即可

pnpm i lodash -w

當然,Monorepo 中除了公共的package.json依賴以外,在每個sub-package子包下面,也會有其私有的package.json依賴

我們本次選擇Pnpm Workspaces 作為 Monorepo 專案的依賴管理工具,一起來搭建一個 monorepo 專案✨

安裝包管理工具

全域性安裝 pnpm

npm i pnpm -g

初始化專案

建立一個新的專案目錄 pnpm-monorepo,根目錄執行 pnpm init 建立 package.json 檔案

然後根目錄新建一個資料夾 packages,用於儲存子包

新建 packages/libc-shared( 共享包 ),用於存放多個專案或元件之間共享的程式碼 。執行 pnpm init 建立 package.json 檔案,修改 package.json 的 name 為 "@libc/shared";修改 package.json 的 main 入口檔案路徑欄位為"src/index.js"

{
  "name": "@libc/shared",
  "version": "1.0.0",
  "main": "src/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
}

新建 packages/libc-ui( 公共元件包 ),即UI元件庫,這裡我們直接 clone 了 iview-ui-plus 程式碼。執行 pnpm install 安裝依賴,

修改 package.json 的 name 為 "@libc/ui";修改 package.json 的 main 入口檔案路徑欄位為"src/index.js"

然後我們在 packages 下建立兩個 vue 專案,vue-dom1vue-dom2,執行指令碼pnpm create vue@latest。由於兩個專案的依賴是完全一樣的,我們可以將 dependencies、devDependencies 複製到外層 package.json 中當做公共依賴,然後pnpm install 安裝一次即可

到了這一步,vue 專案還是不能執行,必須要先配置 workspace,用於支援多包儲存庫💥讓子包 vue 專案可以訪問到我們的公共依賴💥💥

配置workspace

根目錄新建一個 pnpm-workspace.yaml,將 packages 下所有的目錄都作為包進行管理💥💥💥

packages:
  # all packages in direct subdirs of packages/
  - 'packages/*'

pnpm-monorepo 最終專案結構

pnpm-monorepo/
├── packages/
│   ├── libc-shared/
│   ├── libc-ui/
│   ├── vue-dome1/
│   └── vue-dome2/
├── package.json
└── pnpm-workspace.yaml

子包共享💥

此時,pnpm-workspace.yaml工作空間下的每個子包都可以共享我們的公共依賴了。還有個問題是,兄弟模組之間如何共享呢?

之前我們說過,子包之間可以透過 package.json 定義的 name 相互引用,一起看下兩個實際場景

  1. 如何把子包 libc-shared 共享出去?

--workspace引數去安裝共享子包,會去 workspace工作空間中找依賴項並安裝

pnpm install @libc/shared --workspace -w

package.json 中就會自動新增如下依賴,"workspace:" 只會解析本地 workspace 包含的 package

"dependencies": {
   "@libc/shared": "workspace:^"
 }

此時,vue 專案就可以使用公共包 libc-shared 裡的方法,import 引入即可

import { isObject } from '@libc/shared'
  1. 如何把子包 libc-ui 共享出去?

重複一下上面的步驟,然後我們去引用一個 button元件,發現報錯了 Failed to resolve import "./base" from "../libc-ui/src/components/typography/title.vue". Does the file exist?

vite.config.js 中新增 extensions 即可解決,配置一下省略的副檔名列表

resolve: {
  extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}

雖然 button 元件引用成功了,但是發現沒有任何樣式效果。在 libc-ui/src/index.js 檔案中匯入一下樣式檔案就行了

import "./styles/index.less";

依賴

公共依賴

全域性安裝公共依賴 lodash。需要加-w(在工作空間的根目錄中啟動 pnpm)

pnpm install lodash -w

這樣,vue-dom1 和 vue-dom2 這兩個 vue專案就都可以使用 lodash 庫了

區域性依賴

如果只有 vue-dom1 專案用到了 lodash,我們也可以安裝到 vue-dom1 專案內部,不作為公共依賴項,有兩種方法可以實現

  1. cd 到 src/packages/vue-dom1 目錄下,直接安裝
pnpm install lodash
  1. 在任意目錄下,使用 --filter 引數進行安裝;package_selector:package.json 對應的 name 欄位
pnpm install lodash --filter <package_selector>

shamefully-hoist

shamefully-hoist,預設 false

  • false:node_modules下只能看到直接依賴的套件,次級依賴在node_modules/.pnpm 目錄下;無法訪問其他子包區域性安裝的依賴項,例如,vue-dome2 安裝的 lodash,vue-dome1 是訪問不到的

  • true:將所有套件都拉昇到 node_modules 目錄下,能訪問到其他子包區域性安裝的依賴項,例如,vue-dome2 安裝的 lodash,vue-dome1 是能訪問到的

// .npmrc

# pnpm 配置
shamefully-hoist=false

配套程式碼

GitHub - burc-li/pnpm-monorepo: vue3 + pnpm + monorepo 專案 demo 🍎

參考文件

為什麼 pnpm+monorepo 是元件庫專案的最佳實踐

突破專案瓶頸:2024 年 Monorepo 工具選擇和實踐 | BEEZEN

GitHub - Tyh2001/vue3-pnpm-monorepo: 🐠 vue3 + pnpm + monorepo 專案 demo

相關文章