您當前的位置:首頁 > 美食

Quick Start:用 pnpm 管理 Monorepo 專案

作者:由 熊貓 MrPanda 發表于 美食時間:2021-10-24

1、什麼是 Monorepo ?

Monorepo

是管理專案程式碼的方式之一,指在一個大的專案倉庫(repo)中 管理多個模組/包(package),這種型別的專案大都在專案根目錄下有一個packages資料夾,分多個專案管理。大概結構如下:

├── packages

| ├── pkg1

| | ├── package。json

| ├── pkg2

| | ├── package。json

├── package。json

目前很很多大型專案採用這樣的結構,比如:

Babel

vue3

vite

等。

Monorepo 的好處在哪裡嘞?

統一管理

。比如微前端專案,多個子應用可以放在同一個

monorepo

中方便管理;後端用

node。js

的專案放在

monorepo

中也可以使用同一套技術棧管理。在CI/CD等流水線過程中,方便統一迭代或升級版本,也方便做通用化的配置,適用到多個子專案當中。

依賴提升

。如果多個專案都依賴了諸如

react

vue

TypeScript

等常用庫,那可以透過

lerna

或者

yarn workspace

將依賴提升到最外層,多個子模組/包複用同一個依賴,減小專案體積。

2、什麼是 pnpm

非常推薦先閱讀這兩篇文章瞭解 pnpm 的優勢:

zoomdong:Pnpm: 最先進的包管理工具

熊貓MrPanda:【譯】pnpm vs Yarn: monorepo node_modules

在開始之前,可以帶著下面幾個問題閱讀:

什麼是”幻影依賴“和”NPM 分身“?

什麼是”硬連結“和”軟連結“?

pnpm 是如何進行包的快取?

monorepo的場景下,pnpm 如何將依賴提升?

3、快速開始

先全域性安裝一個 pnpm,然後透過:

pnpm init

建立一個專案——在本例中,我們實現一個前後端分離、共同管理的 demo。

首先,我們需要理解 pnpm 中的工作區,在 pnpm 中可以透過建立並配置 pnpm-workspace。yaml 設定 workspace:

packages:

- ‘packages/**’

上面這段配置的意思就是透過 glob 語法將

packages

下的所有資料夾都當做一個package,新增到 monorepo 中進行管理。

接下來,我們就按照配置,建立

packages

資料夾,並在其目錄中建立三個子專案,分別是:web端、node端和工具類。

├── packages

│ ├── server

│ ├── tools

│ └── web

3.1、全域性依賴

我們知道不管是一個 web 專案還是一個 node 專案,它都是基於同一種語言編寫,所以我們可以只安裝一次 TypeScript,供三個專案使用,這就體現出了 monorepo 的優勢。

類似於

TypeScript

lodash

這樣通用的依賴,我們通常可以把他們安裝到根目錄,即使用下面這條命令:

pnpm install typescript -D -W

這裡的

-D

指令大家都很熟悉,就是把依賴作為

devDependencies

安裝;而

-W

就是把依賴安裝到根目錄的

node_modules

當中,目錄結構大致如下:

├── node_modules

│ ├── @types

│ └── typescript -> 。pnpm/typescript@4。4。4/node_modules/typescript

├── package。json

├── packages

│ ├── server

│ ├── tools

│ └── web

├── pnpm-lock。yaml

└── pnpm-workspace。yaml

雖然packages下的專案都沒有安裝 ts,但是倘若在專案中使用到,就會透過依賴遞迴查詢的原則逐級往上尋找,自然會找到 monorepo 中根目錄的依賴。

3.2、區域性依賴

對於某些依賴,可能僅存在於某幾個 package 中,我們就可以單獨為他們安裝,當然,可以透過

cd packges/xxx

後,執行

pnpm install xxx

,但這樣重複操作多次未免有些麻煩,pnpm 提供了一個快捷指令——filter。

比如我們只在 web 應用中用到

vue

,那就可以為它單獨安裝。首先要拿到它的 package name,這個是我們在子專案中自定義的,通常是”@名稱空間+包名@“的方式,比如

@vite/xx

@babel/xx

,在本例中,我們都以@panda開頭。那麼命令如下:

pnpm install vue -r ——filter @panda/web

packages/web/package。json

中,我們可以看到:

“dependencies”: {

“vue”: “^3。0。4”

}

同時,該子專案的

node_modules

中也可以看到

vue

被添加了進來。

3.3、link 機制

在 monorepo 中,我們往往需要 package 間的引用,比如本例中的

@panda/tools

就會被

@panda/server

@panda/web

依賴。

我們可以使用類似於 3。2 中的方法安裝:

pnpm i @panda/tools -r ——filter @panda/server @panda/web

在執行前,我們可以新增一個函式,比如:

export const getNow = () => (new Date()。getTime());

匯出一個函式後,我們執行剛才的安裝指令,這時在專案中會以

workspace

的版本體現。我們檢視web專案中的 package。json:

“dependencies”: {

“@panda/tools”: “workspace:^1。0。0”, // 透過 workspace 為本地引用

“vue”: “^3。0。4”

},

我們再進一步檢視web專案下的 node_modules,可以發現:

Quick Start:用 pnpm 管理 Monorepo 專案

透過軟連結引用了我們的tools package,我們就可以使用了正常的引入和呼叫了!

import { getNow } from ‘@panda/tools’;

getNow();

使用VSCode自帶的快速跳轉,我們可以直接跳轉到tools原始碼定義檔案中,這樣就巧妙地運用了 monorepo 的優勢。

這時你會有一個疑問:當這樣的工具包被髮布到平臺後,如何識別其中的

workspace

呢?

實際上,當執行了

pnpm publish

後,會把基於的workspace的依賴變成外部依賴,如:

//

before

“dependencies”

{

“@panda/tools”

“workspace:^1。0。0”

}

//

after

“dependencies”

{

“@panda/tools”

“^1。0。0”

}

解決了開發環境和生產環境對依賴的問題。

3.4、啟動與開發

一個 monorepo 往往是一個整體的專案,所以我們需要同時執行多個指令,在 pnpm 中,可以透過

-C

進行配置:

“scripts”: {

“start”: “pnpm -C 。/packages/server start:server & pnpm -C 。/packages/web dev”,

}

這條命令的含義就是同時執行服務端和前端程式碼。

start:server

dev

都是我們在子專案的

scripts

中自己配置的。

如果經過了 git 合碼後,專案的依賴變化比較大,可以配置一條 clean 指令:

“scripts”: {

“clean”: “rm -rf node_modules **/*/node_modules”,

}

這樣根目錄和子應用下的

node_modules

都會被刪除。再次執行

pnpm install

也會同時為根目錄和所有 package 安裝需要的依賴。

在實際開發中,我們往往會糾結什麼樣的程式碼應該放在業務專案中,什麼樣的程式碼要單獨抽離為一個 package。舉幾個很簡單的例子:

對於一個大型的應用我們通常需要定製一套元件庫,那麼我們就可以把元件庫最基礎的部分抽離成 package,在業務程式碼中進行二次業務封裝,這樣在其他的專案也能複用。

如果一個專案包含 PC 和 H5,我們不妨把這兩類專案拆分成不同的 package。

如果你在打造一個工具,而它具備了 parser、render等能力,那也可以將不同的能力解耦到不同的 package 當中。

4、總結

總的來說,pnpm 管理 monorepo 已經是當下非常熱門且推薦的做法,已經有很多專案為我們親身實踐過:

Quick Start:用 pnpm 管理 Monorepo 專案

如果你對工程化比較感興趣,或者 npm/yarn 給你的專案管理造成了困擾,都可以嘗試下使用 pnpm 來最佳化你的專案。

標簽: pnpm  package  packages  node  panda