Monorepo設置:新手指南
Monorepo是一種項目代碼管理方法,指在單個代碼倉庫中管理多個項目,有助于簡化代碼共享、版本控制、構建和部署的復雜性,并提供更好的可重用性和協作性。
簡單理解:所有項目都在一個代碼倉庫中 ??,但這并不意味著所有代碼都組織在一個文件夾中 ???。 事實上,一個好的Monorepo與單體代碼庫恰恰相反;它應該結構良好且模塊化。
發展歷程
單體時期
單一代碼倉庫:傳統的單體應用程序通常將所有功能和模塊打包在一起,形成單一的代碼庫和部署單元。這個單一代碼庫包含應用程序的所有部分,從前端界面到后端邏輯,甚至包括數據庫架構和配置文件。
問題:
- 難以實現局部更新和獨立擴展的靈活性 ???
- 高度耦合,代碼臃腫 ??
MultiRepo時代
多個代碼倉庫:不同的功能模塊、組件或服務存儲在獨立的倉庫中,可以獨立進行版本控制、構建、部署和發布,使不同的團隊或開發者能夠獨立開發、測試和維護自己的模塊,更容易實現并行開發和團隊協作。
問題:
- 跨倉庫開發:多倉庫維護成本高
- 開發調試:npm包(修改->發布->安裝成本高),調試麻煩 ??
- 版本管理:依賴版本同步和升級管理麻煩
- 項目基建:腳手架升級難以保證新老項目規范統一 ???
MonoRepo時代
隨著業務復雜度增加,模塊倉庫越來越多。雖然MultiRepo在業務上解耦,但增加了項目工程管理的難度。當模塊倉庫達到一定程度時,會出現以下幾個問題:
- 跨倉庫代碼難以共享
- 單一倉庫中模塊依賴管理分散復雜
- 構建時間增加
因此,將多個項目整合到一個倉庫中,共享項目配置,快速共享模塊代碼,已成為一種趨勢。
Monorepo的優勢
- 代碼復用:因為多個項目共享一個代碼庫,避免了在不同項目中重復編寫相同功能代碼的問題,提高開發效率。
- 提高協作效率:多個項目在同一個代碼庫中開發,可以方便地共享代碼和文檔,避免了不同項目之間的溝通和協調成本。
- 集中管理:在Monorepo架構中,不同的應用程序都在同一個代碼庫中,便于管理和監控。這一點很重要,特別是在需要同時修改和維護多個版本時。
- 統一構建:Monorepo的一個重要特征是可以共享一套構建系統和工具鏈進行構建和部署,提高了構建效率。
- 問題可以快速定位:由于所有代碼都在同一個代碼庫中開發,調試器可以快速找到問題所在的代碼文件和行號,方便開發人員調試問題。
- 一個版本:不用擔心你的項目依賴沖突版本的第三方庫而導致不兼容。
Monorepo的陷阱
幻影依賴
當npm/yarn安裝依賴時,存在依賴提升。一個項目使用的依賴即使沒有在其package.json中聲明也可以直接使用。 這種現象稱為"幻影依賴"。隨著項目迭代,這個依賴不再被其他項目使用而不再安裝。使用幻影依賴的項目會因為找不到依賴而報錯 ??。 基于npm/yarn的Monorepo方案仍然存在"幻影依賴"問題。我們可以通過pnpm完全解決這個問題。
依賴安裝耗時長
MonoRepo中的每個項目都有自己的package.json依賴列表。隨著MonoRepo總依賴數量增長,每次install
都會耗費更長時間 ??。 相同版本的依賴會提升到Monorepo根目錄,以減少重復依賴安裝。所以使用pnpm進行按需安裝和依賴緩存。
Pnpm包管理
為什么選擇pnpm?
Monorepo
單倉庫模塊劃分的需求要求倉庫中的模塊不僅要處理與外部模塊的關系,還要處理內部依賴。因此,我們需要選擇一個強大的包管理工具來幫助處理這些任務。 2022年后,我們推薦使用pnpm來管理項目依賴。它pnpm
涵蓋了大部分的能力,并在多個維度上大大提升了體驗 ??。
Monorepo環境搭建
通過以上內容,我們了解了Monorepo的優勢以及選擇pnpm的原因。 那么如何搭建Monorepo呢? 接下來,讓我們通過Element Plus來學習如何搭建Monorepo環境 ??
首先全局安裝pnpm
npm install pnpm -g
然后使用pnpm init在項目中初始化package.json。這與npm init相同。
pnpm init
獲取package.json的初始內容,然后刪除package.json中的name屬性并添加"private": true
屬性,因為它不需要發布。
{
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
配置pnpm的monorepo工作空間
在我們的倉庫中,我們需要管理多個項目,所以我們可以使用pnpm的monorepo。我們在倉庫根目錄創建一個pnpm-workspace.yaml文件,我們可以在pnpm-workspace.yaml配置文件中指定倉庫中有多少個項目。
packages:
- play # 存放組件測試代碼
- docs # 存放組件文檔
- packages/* # packages目錄下的所有包都是組件包
在packages目錄中,我們可以放置很多package項目目錄,如組件包目錄:
- components
- 主題包目錄:theme-chalk
- 工具包目錄:utils等
然后每個包目錄也需要一個package.json文件來聲明這是一個NPM包目錄。所以我們需要進入每個包目錄初始化一個package.json文件。
以components包為例,我們進入components目錄,初始化一個package.json
文件,并更改包名:@elemnet-plus/components
。文件內容如下:
{
"name": "@elemnet-plus/components",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
其他兩個包分別命名為@elemnet-plus/theme-chalk和@elemnet-plus/utils,創建過程與上述相同。
到目前為止,我們初步的項目目錄結構如下:
├── README.md
├── package.json
├── packages
│ ├── components
│ │ └── package.json
│ ├── theme-chalk
│ │ └── package.json
│ └── utils
│ └── package.json
├── play
└── pnpm-workspace.yaml
倉庫項目包之間相互調用
如果這些包要相互調用,需要將@elemnet-plus/components、@elemnet-plus/theme-chalk、@elemnet-plus/utils安裝到倉庫根目錄下的node_modules目錄中。
然后我們在根目錄中安裝:
pnpm install @elemnet-plus/components -w
pnpm install @elemnet-plus/theme-chalk -w
pnpm install @elemnet-plus/utils -w
-w表示安裝到公共模塊的packages.json中,即根目錄的packages.json中。
安裝后根目錄的package.json內容為:
{
"dependencies": {
"@elemnet-plus/components": "workspace:*",
"@elemnet-plus/theme-chalk": "workspace:*",
"@elemnet-plus/utils": "workspace:*"
},
}
注意:workspace:*這個在將來發布時會被轉換成具體的版本號。
總結
至此,一個通過pnpm配置的monorepo基礎環境已經搭建完成。
什么才是真正的工程化。在配置這個開發環境的過程中,我們似乎只是用一堆工具進行各種配置,那是不是意味著前端工程化就是工具化呢?
實際上,它不僅僅是工具。工程化注重使用工具作為手段來規范工作流程——表達思想、規范項目、有效管理編寫代碼的團隊。