整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ
Monorepo?來聊聊另一種專案管理架構吧!使用 Vite+ pnpm 建立 Vue3 Monorepo
前言
Monorepo 是什麼呢?而這個專案架構又與普通的專案架構有什麼不同呢?剛好最近有相關專案的需求,所以就寫這一篇記錄一下。
什麼是 Monorepo?
Monorepo 是什麼東西呢?首先這是多個單字的組合,分別是 Mono 與 Repo,Mono 代表單一的意思,而 Repo 則是 Repository 的縮寫,也就是說 Monorepo 代表的是單一的 Repository。
感覺滿難懂的吧?沒關係,讓我們先講講其他的專案架構,接著我們再來 Monorepo 吧!
Monolith Repository(單體儲存庫)
通常我們在開發專案的時候,都會歷經以下幾個步驟:
- 建立專案
- 初始化版本控制(
git init) - 開發功能
- 提交變更到遠端儲存庫(GitHub、GitLab、Bitbucket 等)

而這個專案就會獨立一個 Git Repository,也是我們最基本的專案架構,那麼這種架構又稱之為 Monolith Repository(後面僅稱 Monolith),也就是單一專案的 Repository。
那麼 Monolith 的專案架構可能長這樣:
1 | |
透過上面架構我們可以看到會 Monolith 會是一個 Git 管理一整個專案。
雖然 Monolith 的架構非常方便也簡單,在做一些 MVP 專案是相當不錯的首選,可是也會有一些問題存在,當專案開始越來越大時,這個專案就會變得越來越難維護,為什麼呢?舉例來講,我們這個專案裡面可能有包含了以下幾個功能模組:
- 前台首頁
- 前台購物頁面
- 後台管理
…等等功能
開發以及部署的速度會隨著專案越來越龐大而變得越來越慢,就連一個小改動你都需要跑完一整個專案的編譯、測試、部署等等流程,而且只要一個地方出錯,這個專案就會跟你說一聲「掰掰」

那麼為了解決這問題,我們就延伸出了另一個專案架構,也就是 Multi Repository。
Multi Repository(多個儲存庫)
Multi Repository(後面簡稱 Multi)是什麼呢?簡單來講就是將原本的 Monolith 拆分成多個 Repository,也就是將原本的專案拆分成多個專案,每個專案都有自己的 Repository,這樣就可以讓每個專案獨立開發、獨立部署,這樣就可以解決 Monolith 的問題。
那麼這個專案架構就會從原本的 Monolith 變成以下的架構:
1 | |
看起來這種專案架構是相當不錯的,讓我們解決了牽一髮而動全身的問題,而這個專案架構也會將功能模組各自拆分成一個專案,這樣就可以讓每個專案獨立開發、獨立部署,這樣就可以解決 Monolith 的問題。
但卻也有一些問題發生,我們在上面的例子中,我們可以看到 Button、Input 這兩個功能模組都是重複的,這樣就會造成重複開發的問題,而且當這兩個功能模組有一個地方出錯時,我們就需要到每個專案去修改,這樣就會造成維護上的困難,當然不只有這個問題存在,像是專案的 CI/CD、套件的版本等等也就會變得更加難以維護 QAQ…

Mono Repository(單一儲存庫)
那麼為了解決這個問題,所以又延伸出了另一個專案架構,也就是我們這一篇要說的重點 Monorepo 架構。
這時候應該有點混亂了,所以我決定畫圖來讓大家更加了解彼此的架構差異,首先是 Monolith:

我們可以看到 Monolith 的專案架構是一個專案一個 Repository,裡面通常會包含了許多功能模組,這樣就會造成牽一髮而動全身的問題。
接下來是 Multi:

我們可以看到 Multi 的專案架構是將原本的 Monolith 拆分成多個 Repository,這樣就可以讓每個專案獨立開發、獨立部署,這樣就可以解決 Monolith 的問題。
最後是 Mono Repository(後面簡稱 Monorepo):

透過圖像我們可以看到原本 Multi 都是獨立的 Git 管理,但是在 Monorepo 中,我們可以看到所有的專案都是在同一個 Repository 中並且由一個 Git 管理,這樣就可以解決 Multi 的問題,讓我們可以將所有的專案都放在同一個 Repository 中,讓所有的專案共享相同的資源。
雖然 Monorepo 的架構解決了 Multi 的問題,但也有一些缺點,例如…
- 專案肥大後
git clone時會花費較多時間 - 共用的東西必須要規劃好,否則容易互相影響
- 由於 Monorepo 中的專案都是共用的,所以無法區分專案的權限
- Git 儲存庫的大小會變得非常大
我們可以發現不論是哪一種專案架構,都有屬於他的優缺點,所以在選擇專案架構時,可以評估一下自己的專案需求,再來選擇適合的專案架構。
用 Vite 實作一個 Monorepo
接下來我將會介紹如何使用 Vite + pnpm 建立一個 Vue3 的 Monorepo,首先請你先依照以下步驟準備好環境:
1 | |
接下來我們要做一點特別的設定,在 pnpm 的 工作空間(Workspace) 官方文件有說到一件事情,如果我們要建立 Monorepo 的話會需要在根目錄建立一個「pnpm-workspace.yaml」檔案,所以請你在這個專案底下建立一個「pnpm-workspace.yaml」檔案。
Note
pnpm-workspace.yaml 這個檔案主要是用來告訴 pnpm 這個專案是一個 Monorepo 專案,並且在你執行pnpm install時會自動安裝所有專案的相依套件
內容要寫什麼呢?這邊先讓我們回頭看一下 Monorepo 的圖:

我們可以看到一個 Git 底下會管理很多個專案,所以通常 Monorepo 的專案會長這樣:
1 | |
我們可以看到有一個 apps 資料夾,這個就是我們將會放置所有專案的地方,所以我們要在根目錄建立一個「apps」資料夾
1 | |
接著在「pnpm-workspace.yaml」檔案中寫入以下內容:
1 | |
到這邊為止,已經將 Monorepo 的基本環境準備好了,接下來就是建立一個 Vue3 的專案,請你移動到「apps」資料夾中,並且建立一個 Vue3 的專案:
1 | |
這邊我就示範建立一個「shopping」的專案

建立完成後,你專案應該會長這樣:

接下來要請你打開 apps/shopping 的專案,並且找到 package.json 將 name 屬性改成 @apps/shopping,這樣才能讓 pnpm 知道這個專案是屬於 Monorepo 底下的專案。
1 | |
Note
@app/專案名稱這個是 Monorepo 的命名規則,專案名稱在 Monorepo 中是不能重複的,所以通常 Monorepo 底下的命名為「@」+「apps」+「/」+「專案名稱」,例如…我要建立一個 shopping 的專案,那麼專案名稱就會是「@apps/shopping」。
接下來請你回到最外層找到 package.json,並增加一個啟動指令在 scripts 中:
1 | |
pnpm --filter @apps/shopping 這一段的指令意思是告訴 pnpm 這個專案是要執行 @apps/shopping 這個專案,這樣就可以讓我們在根目錄執行 pnpm shopping 時,就會啟動 @apps/shopping 這個專案。
接下來我們該如何啟動專案呢?只需要在根目錄執行以下指令即可:
1 | |
Note
pnpm shopping dev這個指令的意思是告訴 pnpm 這個專案是要執行@apps/shopping這個專案,並且執行dev這個指令,dev指令則是@apps/shopping專案中的scripts中的dev指令。
建立公用依賴套件
接下來由於我們底下環境都是 Vue 相關的:
1 | |
所以我們就可以把 dependencies 跟 devDependencies 移動外層 package.json,作為一個共用,接下來只要將相關的指令加入到 scripts 即可,所以調整後會變成以下:
1 | |
Note
由於我們將dependencies跟devDependencies移動到根目錄,所以才把--filter改成了-C,這樣才能讓 pnpm 知道這個專案是要執行哪個專案。
接下來輸入指令時,只需要輸入相對應的指令即可:
1 | |

那麼如果我們想要新增一個公用的套件,只需要在根目錄下安裝即可,例如…vue-loading-overlay 套件
1 | |
Note
在 Monorepo 中安裝套件時,需要加上-w參數,這樣才能讓 pnpm 知道這個套件是要安裝在所有專案中,如果是安裝 devDependencies 則需要加上-D參數,如:pnpm add eslint -w -D。
建立公用的 Vue Component
假設今天我們有一個共用的 Button 元件,那我們在 Monorepo 該如何處理呢?首先請你在根目錄輸入以下指令
1 | |
接著輸入以下指令一個新的 Vue3 專案
1 | |
接著把專案精簡成以下:

因為我們是要共用元件,並不需要 vite.config.js、public 以及 App.vue 這類檔案,接著打開 ui-lib 將 package.json 改成以下即可:
1 | |
Note
main屬性非常重要,請務必指向到你的 main.js 檔案,這樣才能讓其他專案引入這個套件。
接下來該如何在 Shopping 使用呢?只需要輸入以下指令:
1 | |
接著在 Shopping 的專案中使用這個元件即可:
1 | |
這樣就可以在 Shopping 中使用 ui-lib 的 Button 元件了~
那麼以上就差不多是一個簡單的 Monorepo 環境,最後我也提供這個範例的 GitHub 連結給你參考。
Note
git clone專案之後,只需要在根目錄輸入pnpm install即可安裝所有專案的相依套件。
整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ