轉發:基於pnpm + lerna + typescript的最佳實踐
Part1 Pnpm
pnpm
是一款當代受歡迎 新興(問題較多) 的包管理工具。
為什麼會出現pnpm
?因為yarn
的出現並沒有滿足作者的一些期待,反而有些失望。
After a few days, I realized that Yarn is just a small improvement over npm. Although it makes installations faster and it has some nice new features, it uses the same flat node_modules structure that npm does (since version 3). And flattened dependency trees come with a bunch of issues 幾天後,我意識到 Yarn 只是對 npm 的一個小小的改進。儘管它使安裝速度更快,並且具有一些不錯的新功能,但它使用與npm相同的平面node_modules
結構(自版本 3 起)。扁平化的依賴樹帶來了一系列問題
(具體後面會講)
為什麼叫pnpm
?是因為pnpm
作者對現有的包管理工具,尤其是npm
和yarn
的效能比較特別失望,所以起名叫做perfomance npm
,即pnpm
(高效能npm)
如何突顯pnpm
的效能優勢?在pnpm
官網上,提供了一個benchmarks圖表,它比對了專案[1]在npm、pnpm、yarn(正常版本和PnP版)中,install
、update
場景下的耗時:
image.png
下面表格是上圖中的具體資料:
action |
cache |
lockfile |
node_modules |
npm |
pnpm |
Yarn |
Yarn PnP |
---|---|---|---|---|---|---|---|
install |
1m 12.2s |
15.7s |
22.1s |
27.5s |
|||
install |
✔ |
✔ |
✔ |
1.6s |
1.3s |
2.6s |
n/a |
install |
✔ |
✔ |
9.5s |
4s |
8.6s |
1.9s |
|
install |
✔ |
14.2s |
7.9s |
14.2s |
7.4s |
||
install |
✔ |
25.4s |
13s |
15.3s |
21.1s |
||
install |
✔ |
✔ |
2.1s |
1.8s |
8.3s |
n/a |
|
install |
✔ |
✔ |
1.6s |
1.4s |
9.4s |
n/a |
|
install |
✔ |
2.1s |
5.9s |
15s |
n/a |
||
update |
n/a |
n/a |
n/a |
1.6s |
12.1s |
18.7s |
32.4s |
可以看到pnpm(橘色)
有很明顯效能提升,在我們專案實踐中(基於gitlib
)提升更明顯(跟store dir
搭配使用)
在討論效能提升原因之前,我們先了解下現有包管理工具中node_modules
存在的問題
1node_modules 結構
Nested installation 巢狀安裝
在 npm@3 之前,node_modules結構是乾淨
、可預測
的,因為node_modules 中的每個依賴項都有自己的node_modules資料夾,在package.json中指定了所有依賴項。例如下面所示,專案依賴了foo
,foo
又依賴了bar
,依賴關係如下圖所示:
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
上面結構有兩個嚴重的問題:
- package中經常建立太深的依賴樹,這會導致 Windows 上的目錄路徑過長問題
- 當一個package在不同的依賴項中需要時,它會被多次複製貼上並生成多份檔案
Flat installation 扁平安裝
為了解決上述問題,npm 重新考慮了node_modules結構並提出了扁平化結構。在npm@3+ 和 yarn中,node_modules 結構變成如下所示:
node_modules
├─ foo
| ├─ index.js
| └─ package.json
└─ bar
├─ index.js
└─ package.json
可以看到,hoist
機制下,bar
被提升到了頂層。如果同一個包的多個版本在專案中被依賴時,node_modules結構又是怎麼樣的?
例如:一個專案App
直接依賴了A(version: 1.0)
和C(version: 1.0)
,A
和C
都依賴了不同版本的B
,其中A
依賴B 1.0
,C
依賴B 2.0
,可以通過下圖清晰的看到npm2
和npm3+
結構差異:
image.png
包B 1.0
被提升到了頂層,這裡需要注意的是,多個版本的包只能有一個
被提升上來,其餘版本的包會巢狀安裝到各自的依賴當中(類似npm2
的結構)。
至於哪個版本的包被提升,依賴於包的安裝順序!
依賴變更會影響提升的版本號,比如變更後,有可能是B 1.0
,也有可能是 B 2.0
被提升上來(但只能有一個版本提升)
細心的小夥伴可能發現,這其實並沒有解決之前的問題,反而又引入了新的問題
npm3+和yarn存在的問題
Phantom dependencies 幽靈依賴
Phantom dependencies 被稱之為幽靈依賴或幻影依賴,解釋起來很簡單,即某個包沒有在package.json
被依賴,但是使用者卻能夠引用到這個包。
引發這個現象的原因一般是因為 node_modules 結構所導致的。例如使用 npm或yarn 對專案安裝依賴,依賴裡面有個依賴叫做 foo
,foo
這個依賴同時依賴了 bar
,yarn 會對安裝的 node_modules 做一個扁平化結構的處理,會把依賴在 node_modules 下打平,這樣相當於 foo
和 bar
出現在同一層級下面。那麼根據 nodejs 的尋徑原理,使用者能 require 到 foo
,同樣也能 require 到 bar
。
nodejs的定址方式:(檢視更多[2])
- 對於核心模組(core module) => 絕對路徑 定址
- node標準庫 => 相對路徑定址
- 第三方庫(通過npm安裝)到node_modules下的庫: 3.1. 先在當前路徑下,尋找 node_modules/xxx 3.2 遞迴從下往上到上級路徑,尋找 ../node_modules/xxx 3.3 迴圈第二步 3.4 在全域性環境路徑下尋找 .node_modules/xxx
NPM doppelgangers NPM分身
這個問題其實也可以說是 hoist 導致的,這個問題可能會導致有大量的依賴的被重複安裝.
舉個例子:專案中有packageA
、packageB
、packageC
、packageD
。packageA
依賴packageX 1.0和packageY 1.0,packageB
依賴packageX 2.0和packageY 2.0,packageC
依賴packageX 1.0和packageY 2.0,packageD
依賴packageX 2.0和packageY 1.0。
在npm2時,結構如下
- package A
- packageX 1.0
- packageY 1.0
- package B
- packageX 2.0
- packageY 2.0
- package C
- packageX 1.0
- packageY 2.0
- package D
- packageX 2.0
- packageY 1.0
在npm3+和yarn中,由於存在hoist機制,所以X和Y各有一個版本被提升了上來,目錄結構如下
- package X => 1.0版本
- package Y => 1.0版本
- package A
- package B
- packageX 2.0
- packageY 2.0
- package C
- packageY 2.0
- package D
- packageX 2.0
如上圖所示的packageX 2.0和packageY 2.0被重複安裝多次,從而造成 npm 和 yarn 的效能一些效能損失。
這種場景在monorepo 多包場景下尤其明顯,這也是yarn workspace
經常被吐槽的點,另外扁平化的演算法實現也相當複雜,改動成本很高。 那麼pnpm
是如何解決這種問題的呢?
2pnpm的破解之道:網狀 + 平鋪的node_modules結構
一些背景知識:inode、hardl link和symbolic link的基礎概念[3]
pnpm
的使用者可能會發現它node_modules
並不是扁平化結構,而是目錄樹的結構,類似npm version 2.x
版本中的結構,如下圖所示
image.png
同時還有個.pnpm
目錄,如下圖所示
image.png
.pnpm
以平鋪的形式儲存著所有的包,正常的包都可以在這種命名模式的資料夾中被找到(peerDep例外):
.pnpm/<organization-name>+<package-name>@<version>/node_modules/<name>
// 組織名(若無會省略)+包名@版本號/node_modules/名稱(專案名稱)
我們稱.pnmp
為虛擬儲存目錄,該目錄通過<package-name>@<version>
來實現相同模組不同版本之間隔離和複用,由於它只會根據專案中的依賴生成,並不存在提升,所以它不會存在之前提到的Phantom dependencies問題!
那麼它如何跟檔案資源進行關聯的呢?又如何被專案中使用呢?
答案是Store
+ Links
!
Store
pnpm
資源在磁碟上的儲存位置。
pnpm
使用名為 .pnpm-store的 store dir[4],Mac/linux中預設會設定到{home dir}>/.pnpm-store/v3
;windows下會設定到當前盤的根目錄下,比如C(C/.pnpm-store/v3
)、D盤(D/.pnpm-store/v3
)。
具體可以參考 @pnpm/store-path
這個 pnpm
子包中的程式碼:
const homedir = os.homedir()
if (await canLinkToSubdir(tempFile, homedir)) {
await fs.unlink(tempFile)
// If the project is on the drive on which the OS home directory
// then the store is placed in the home directory
return path.join(homedir, relStore, STORE_VERSION)
}
由於每個磁碟有自己的儲存方式,所以Store會根據磁碟來劃分。 如果磁碟上存在主目錄,儲存則會被建立在
<home dir>/.pnpm-store
;如果磁碟上沒有主目錄,那麼將在檔案系統的根目錄中建立該儲存。 例如,如果安裝發生在掛載在/mnt
的檔案系統上,那麼儲存將在/mnt/.pnpm-store
處建立。 Windows系統上也是如此。
可以在不同的磁碟上設定同一個儲存,但在這種情況下,pnpm
將複製包而不是硬連結它們,因為硬連結只能發生在同一檔案系統同一分割槽上。
windows store如下圖所示
image.png
pnpm install
的安裝過程中,我們會看到如下的資訊,這個裡面的Content-addressable store
就是我們目前說的Store
image.png
CAS 內容定址儲存,是一種儲存資訊的方式,根據內容而不是位置進行檢索資訊的儲存方式。
Virtual store 虛擬儲存,指向儲存的連結的目錄,所有直接和間接依賴項都連結到此目錄中,專案當中的.pnpm目錄
如果是 npm 或 yarn,那麼這個依賴在多個專案中使用,在每次安裝的時候都會被重新下載一次
如圖可以看到在使用 pnpm 對專案安裝依賴的時候,如果某個依賴在 sotre 目錄中存在了話,那麼就會直接從 store 目錄裡面去 hard-link,避免了二次安裝帶來的時間消耗,如果依賴在 store 目錄裡面不存在的話,就會去下載一次。
當然這裡你可能也會有問題:如果安裝了很多很多不同的依賴,那麼 store 目錄會不會越來越大?
答案是當然會存在,針對這個問題,pnpm 提供了一個命令來解決這個問題: pnpm store | pnpm[5]。
同時該命令提供了一個選項,使用方法為 pnpm store prune
,它提供了一種用於刪除一些不被全域性專案所引用到的 packages 的功能,例如有個包 [email protected]
被一個專案所引用了,但是某次修改使得專案裡這個包被更新到了 1.0.1
,那麼 store 裡面的 1.0.0 的 axios 就就成了個不被引用的包,執行 pnpm store prune
就可以在 store 裡面刪掉它了。
該命令推薦偶爾進行使用,但不要頻繁使用,因為可能某天這個不被引用的包又突然被哪個專案引用了,這樣就可以不用再去重新下載這個包了。
看到這裡,你應該對Store有了一些簡單的瞭解,接著我們來看下專案中的檔案如何跟Store關聯。
Links(hard link & symbolic link)
還記得文章剛開始,放了兩張beachmark的圖表,圖表上可以看到很明顯的效能提升(如果你使用過,感觸會更明顯)!
pnpm 是怎麼做到如此大的提升的呢?一部分原因是使用了計算機當中的 Hard link[6] ,它減少了檔案下載的數量,從而提升了下載和響應速度。
hard link 機制
通過hard link
, 使用者可以通過不同的路徑引用方式去找到某個檔案,需要注意的是一般使用者許可權下只能硬連結到檔案,不能用於目錄。
pnpm
會在Store
(上面的Store) 目錄裡儲存專案 node_modules
檔案的 hard links
,通過訪問這些link直接訪問檔案資源。
舉個例子,例如專案裡面有個 2MB 的依賴 react
,在 pnpm 中,看上去這個 react
依賴同時佔用了 2MB 的 node_modules 目錄以及全域性 store 目錄 2MB 的空間(加起來是 4MB),但因為 hard link
的機制使得兩個目錄下相同的 2MB 空間能從兩個不同位置進行CAS定址
直接引用到檔案,因此實際上這個react
依賴只用佔用2MB 的空間,而不是4MB。
因為這樣一個機制,導致每次安裝依賴的時候,如果是個相同的依賴,有好多專案都用到這個依賴,那麼這個依賴實際上最優情況(即版本相同)只用安裝一次。
而在npm
和yarn
中,如何一個依賴被多個專案使用,會發生多次下載和安裝!
如果是 npm 或 yarn,那麼這個依賴在多個專案中使用,在每次安裝的時候都會被重新下載一次。
image.png
如圖可以看到在使用 pnpm 對專案安裝依賴的時候,如果某個依賴在 sotre 目錄中存在了話,那麼就會直接從 store 目錄裡面去 hard-link,避免了二次安裝帶來的時間消耗,如果依賴在 store 目錄裡面不存在的話,就會去下載一次。
通過Store
+ hard link
的方式,不僅解決了專案中的NPM doppelgangers問題,專案之間也不存在該問題,從而完美解決了npm3+
和yarn
中的包重複問題!
如果隨著專案越來越大,版本變更變多,歷史版本的資源會堆積,導致Store
目錄越來越大,那如何解決這個問題呢?
針對這個現象,pnpm 提供了一個命令來解決這個問題: pnpm store | pnpm[7]。
同時該命令提供了一個選項,使用方法為 pnpm store prune
,它提供了一種用於刪除一些不被全域性專案所引用到的 packages 的功能,例如有個包 [email protected]
被一個專案所引用了,但是某次修改使得專案裡這個包被更新到了 1.0.1
,那麼 store 裡面的 1.0.0 的 axios 就就成了個不被引用的包,執行 pnpm store prune
就可以在 store 裡面刪掉它了。
該命令推薦偶爾進行使用,但不要頻繁使用,因為可能某天這個不被引用的包又突然被哪個專案引用了,這樣就可以不用再去重新下載這個包了。
symbolic link
由於hark link
只能用於檔案不能用於目錄,但是pnpm
的node_modules
是樹形目錄結構,那麼如何連結到檔案? 通過symbolic link
(也可稱之為軟鏈或者符號連結)來實現!
通過前面的講解,我們知道了pnpm
在全域性通過Store
來儲存所有的node_modules依賴,並且在.pnpm/node_modules
中儲存專案的hard links,通過hard link
來連結真實的檔案資源,專案中則通過symbolic link
連結到.pnpm/node_modules
目錄中,依賴放置在同一級別避免了迴圈的軟鏈。
pnpm
的 node_modules
結構一開始看起來很奇怪:
- 它完全適配了 Node.js。
- 包與其依賴被完美地組織在一起。
有 peer 依賴的包的結構更加複雜[8]一些,但思路是一樣的:使用軟鏈與平鋪目錄來構建一個巢狀結構。
假設我們有個mono repo,它有repo A
、repo B
、repo C
和repo D
4個repo。每個repo有各自的一些依賴項(包括dependencies和peerDependencies),假定結構如下圖所示:(需要注意有個peer dep)
image.png
下面是pnpm workspace
中,比較清晰(不清晰的話留言,我可以改改!)說明了Store
和Links
間的相互關係:
image.png
官網也更新了類似的呼叫關 圖,大家也可以看看!
image.png
PeerDependencies
pnpm 的最佳特徵之一是,在一個專案中,package
的一個特定版本將始終只有一組依賴項。 這個規則有一個例外 -那就是具有 peer dependencies [9]的package
。
通常,如果一個package
沒有 peer 依賴項(peer dependencies),它會被硬連結到其依賴項的軟連線(symlinks)旁的 node_modules
,就像這樣:
image.png
如果 foo
有 peer 依賴(peer dependencies),那麼它可能就會有多組依賴項,所以我們為不同的 peer 依賴項建立不同的解析:
image.png
pnpm
建立[email protected][email protected][email protected]
或[email protected][email protected][email protected]
內到foo
的軟連結。 因此,Node.js 模組解析器將找到正確的 peers。
peerDep
的包命名規則如下(看起來就很麻煩)
.pnpm/<organization-name>+<package-name>@<version>_<organization-name>+<package-name>@<version>/node_modules/<name>
// peerDep組織名(若無會省略)+包名@版本號_組織名(若無會省略)+包名@版本號/node_modules/名稱(專案名稱)
如果一個
package
沒有 peer 依賴(peer dependencies),不過它的依賴項有 peer 依賴,這些依賴會在更高的依賴圖中解析, 則這個傳遞package
便可在專案中有幾組不同的依賴項。 例如,[email protected]
具有單個依賴項[email protected]
。[email protected]
有一個 peer 依賴為c@^1
。[email protected]
永遠不會解析[email protected]
的 peer, 所以它也會依賴於[email protected]
的 peer 。
如果需要解決peerDep引入的多例項問題,可以通過 `.pnpmfile.cjs`[10]檔案更改依賴項的依賴關係。
Part2 Lerna
Lerna 是一個管理工具,用於管理包含多個軟體包(package)的 JavaScript 專案。它自身功能非常強大,特別是版本變更、專案釋出等功能,可以滿足各種複雜場景的訴求,除了workspace
經常被人吐槽(可以使用yarn workspace
),是業界當中使用規模最多的repo管理工具。
因為專案中要使用import
帶入、version
版本變更、publish
專案釋出功能,所以著重介紹這三個命令,想要了解更多的同學可以去官網檢視
3lerna import
將一個包內容(包括提交歷史記錄)匯入到monorepo
中。
命令如下:
lerna import <path-to-external-repository> --preserve-commit
<path-to-external-repository>
是原生代碼的儲存目錄,執行後匯入到packages/<directory-name>
中,包括原始提交作者、日期和訊息。執行前需要確保分支的正確性(一般是master分支),之後匯入就會自動執行。
需要注意目前僅支援本地匯入
,遠端匯入的話需要使用一些其他技巧。
image.png
--preserve-commit
選項,使用該配置項可以保留原始提交者和提交日期,從而避免下面的問題。
每次git提交都有一位作者和一位提交者(每人都有一個單獨的日期)。通常他們是同一個人(和日期),但是因為lerna import從外部儲存庫重新建立每個提交,提交者就變成了當前的git使用者(和日期)。這在技術上是正確的,但邏輯上不對,例如,在 Github 上,如果作者和提交者是不同的人,它就會同時顯示他們,這可能會導致匯入提交時的歷史/職責出現混亂。
import
命令對歷史程式碼遷移到mono repo倉庫特比有用。同時對每次歷史變更為相對包目錄進行修改。例如,新增package.json
的提交將改為新增packages/<directory-name>/package.json
。
4lerna version
修改自上次釋出以來的包版本號。
為方便同學們學習,先簡單介紹下語義化版本。
語義化版本
前端中的包應該遵循
語義化版本
(也稱為“semver”,來源於荷蘭語)規範。當你從registry安裝package時,它將會使用語義化的版本新增到專案的package.json
中。
這些版本被分解major.minor.patch
為以下其中之一:3.14.1
, 0.42.0
, 2.7.18
。版本的每個部分隨時間或者功能進行變更:
-
major
主版本號:當你做了不相容的 API 修改。 -
minor
次版本號:當你做了向下相容的功能性新增。 -
patch
修訂版本號:當你做了向下相容的問題修正。 注意: 有時還有 semver 格式的“標籤”或“擴充套件”,用於標記預釋出或測試版(例如2.0.0-beta.3
)
當開發人員談論兩個 semver 版本彼此“相容”時,他們指的是向後相容的更改(minor
和 patch
)
工作模式
Lerna
有兩種工作模式:Fixed mode
和Independent mode
Fixed/Locked mode (default) 固定/鎖定模式
固定模式下 Lerna 專案全域性僅有一個版本號。該版本號在專案根目錄下的lerna.json
檔案中version
屬性中維護。執行lerna publish
時,如果模組從上次釋出以來有能觸發發版行為的更新,則version
會修改為要釋出的新版本。這意味著可以僅在需要時釋出包的新版本。
注意:如果主版本為零,則所有更新都被視為
破壞性修改(Breaking change)
. 因此,lerna publish
以零為主要版本執行並選擇任何非預釋出版本號將導致為所有包釋出新版本,即使自上次釋出以來並非所有包都已更改。
這是Babel[11]目前使用的模式。如果您想自動將所有軟體包版本繫結在一起,請使用此選項。
這種方法的存在兩個問題:
- 任何包的重大更改都會導致所有包都具有新的主要版本。
- 專案中包的版本變更會非常快
這些都是一致性帶來的衍生效應,具體大家可以自行評估
Independent mode 獨立模式
將lerna.json
檔案中的version
設定為independent
,即可以獨立模式執行。 專案初始化時,可以通過一下命令設定獨立模式:
lerna init --independent
獨立模式的 Lerna 專案允許各個包維護自己的版本。每次釋出時,都會收到有關已更改的包
的提示,以指定它是補丁、次要、主要還是自定義更改。
獨立模式允許您更具體地更新每個包的版本並使每次更新有各自的意義。將這種模式與semantic-release[12]之類的東西結合起來會減少痛苦。(在atlassian/lerna-semantic-release[13]已經有這方面的工作)。
預釋出
如果你有一個預釋出版本號的軟體包(例如2.0.0-beta.3
),並且你運行了lerna version
和一個非預先發布的版本(major
、minor
或patch
),它將會發布那些之前釋出的軟體包以及自上次釋出以來已經改變的軟體包。
對於使用常規提交的專案,使用以下標誌進行預發行管理:
- --conventional-prerelease[14]: 將當前更改作為預發行版本釋出。
- --conventional-graduate[15]: 將預釋出版本的包升級為穩定版本。
如果不使用上面的引數執行lerna version --conventional-commits
,則只有在版本已經在prerelease
中時,才會將當前更改作為prerelease
釋放。
生命週期
// preversion: 在設定版本號之前執行.
// version: 在設定版本號之後,提交之前執行.
// postversion: 在設定版本號之後,提交之後執行.
lerna 將在lerna version
期間執行npm 生命週期指令碼[16]:
- 偵測更改的包,選擇版本號進行覆蓋。
- 在根目錄執行
preversion
。 - 對於每個更改的包,按照拓撲順序(所有依賴項在依賴關係之前): i. 執行
preversion
生命週期。 ii. 更新 package.json 中的版本。 iii. 執行version
生命週期。 - 在根目錄執行
version
生命週期。 - 如果可用[17],將更改檔案新增到索引。
- 如果可用[18]建立提交和標記。
- 對於每個改變包,按照詞法順序(根據目錄結構的字母順序): i. 執行
postversion
生命週期。 - 在根目錄執行
postversion
。 - 如果可用[19]推動提交和標記到遠端伺服器。(該流程會觸發
git push
操作) - 如果可用[20]建立釋出。
5lerna publish
在當前專案中釋出所有包
lerna publish # 釋出自上一個版本以來發生了變化的包
lerna publish from-git # 釋出當前提交中標記的包
lerna publish from-package # 釋出登錄檔中沒有最新版本的包
在執行時,該命令做了下面幾件事中的一個
- 釋出自上一個版本以來更新的包(背後呼叫了lerna version[21])。
- 這是 lerna 2.x 版本遺留下來的。
- 釋出在當前提交中標記的包(
from-git
)。 - 釋出在最新提交時登錄檔中沒有版本的包(
from-package
)。 - 釋出在前一次提交中更新的包(及其依賴項)的“金絲雀(canary)”版。
注意: Lerna 永遠不會釋出標記為private
的包(package.json
中的”private“: true
)
在所有的釋出過程中,都有生命週期[22]在根目錄和每個包中執行(除非使用了--ignore-scripts
)。
釋出方式
from-git
除了 lerna version[23] 支援的語義化版本關鍵字外,lerna publish
也支援from-git
關鍵字。這將會識別lerna version
標記的包,並將它們釋出到 npm。這在您希望手動增加版本的 CI 場景中非常有用,但要通過自動化過程一直地釋出包內容本身。
from-package
與from-git
關鍵字類似,只是要釋出的包列表是通過檢查每個package.json
確定的,並且要確定登錄檔中是否存在任意版本的包。登錄檔中沒有的任何版本都將被髮布。當前一個lerna publish
未能將所有包釋出到登錄檔時,就是他發揮的時候了。
生命週期
// prepublish: 在打包和釋出包之前執行。
// prepare: 在打包和釋出包之前執行,在 prepublish 之後,prepublishOnly 之前。
// prepublishOnly: 在打包和釋出包之前執行,只在 npm publish 時執行。
// prepack: 只在原始碼壓縮打包之前執行。
// postpack: 在原始碼壓縮打包生成並移動到最終目的地後執行。
// publish: 在包釋出後執行。
// postpublish: 在包釋出後執行。
Lerna 將在
lerna publish
期間執行npm生命週期指令碼[24],順序如下
- 如果採用沒有指定版本,則執行所有版本生命週期指令碼[25]
- 如果可用[26],在根目錄執行
prepublish
生命週期。 - 在根目錄中執行
prepare
生命週期。 - 在根目錄中執行
prepublishOnly
生命週期。 - 在根目錄執行
prepack
生命週期。 - 對於每個更改的包,按照拓撲順序(所有依賴項在依賴關係之前): i. 如果可用[27],執行
prepublish
生命週期。 ii. 執行prepare
生命週期。 iii. 執行prepublishOnly
生命週期。 iv. 執行prepack
生命週期。 v. 通過JS API[28]在臨時目錄中建立原始碼壓縮包。 vi. 執行postpack
生命週期。 - 在根目錄執行
postpack
生命週期。 - 對於每個更改的包,按照拓撲順序(所有依賴項在依賴關係之前): i. 通過JS API[29]釋出包到配置的登錄檔[30]。 ii. 執行
publish
生命週期。 iii. 執行postpublish
生命週期。 - 在根目錄中執行
publish
生命週期。- 為了避免遞迴呼叫,不要使用這個根生命週期來執行
lerna publish
。
- 為了避免遞迴呼叫,不要使用這個根生命週期來執行
- 在根目錄中執行
postpublish
生命週期。 - 如果可用[31],將臨時的 dist-tag 更新到最新
6指令總覽 (Commands)
指令參考地址(漢化)[32]
指令 |
解釋 |
連結(英文) |
---|---|---|
lerna publish |
在當前專案中釋出包 |
前往[33] |
lerna version |
更改自上次釋出以來的包版本號 |
前往[34] |
lerna bootstrap |
將本地包連結在一起並安裝剩餘的包依賴項 |
前往[35] |
lerna list |
列出本地包 |
前往[36] |
lerna changed |
列出自上次標記釋出以來發生變化的本地包 |
前往[37] |
lerna diff |
自上次釋出以來的所有包或單個包的區別 |
前往[38] |
lerna exec |
在每個包中執行任意命令 |
前往[39] |
lerna run |
在包含該指令碼中的每個包中執行npm指令碼 |
前往[40] |
lerna init |
建立一個新的Lerna倉庫或將現有的倉庫升級到Lerna的當前版本 |
前往[41] |
lerna add |
向匹配的包新增依賴關係 |
前往[42] |
lerna clean |
從所有包中刪除node_modules目錄 |
前往[43] |
lerna import |
將一個包匯入到帶有提交歷史記錄的monorepo中 |
前往[44] |
lerna link |
將所有相互依賴的包符號連結在一起 |
前往[45] |
lerna create |
建立一個新的由lerna管理的包 |
前往[46] |
lerna info |
列印本地環境資訊 |
前往[47] |
Part3 Typescript
TypeScript
是JavaScript型別的超集,他可以編譯成純JavaScript。
TypeScript
可以在任何瀏覽器、任何計算機和任何作業系統上執行,並且是開源的。
TS介紹的人已經相當多了,這裡就不再介紹了!強力安利一波,用過的人都說香!
Part4 總結
pnpm
將來會成為主流的registry管理工具,這個毋庸置疑。現在pnpm
的下載量已經擊敗了Bower,並且2021年的下載量是2020年的3
倍,目前已經擁有了14.6k的Star。yarn
和npm
已經開始參考pnpm
的設計並進行改進,vue
、vite
等框架也開始為pnpm
背書,還沒有用過pnpm
的同學可以嘗試下,相信你一定會喜歡它!
Yarn 在 v3.1[48] 添加了 pnpm 連結器。 因此 Yarn 可以建立一個類似於 pnpm 建立的 node_modules 目錄結構。此外,Yarn 團隊計劃實現內容可定址儲存,以提高磁碟空間效率。
Npm 團隊決定也採用 pnpm 使用的符號連結的 node_modules 目錄結構(相關 RFC[49])。
lerna
強大的版本管理能力,完全可以彌補pnpm
在包管理上的弱勢,兩者協同支援的的呼喊聲也越來越強烈,相信將來lerna
+ pnpm
一定會成為最佳monorepo管理方案之一。