基於 pnpm - lerna - typescript 的最佳項目實踐 - 理論篇
本文來自作者 @金虹橋程序員 投稿
原文鏈接:https://juejin.cn/post/7043998041786810398
本系列文章分爲兩篇:
理論篇和實踐篇
理論篇:介紹 pnpm(pnpm 的特點、解決的問題等)、lerna(lerna 的常用命令)、typescript
實踐篇:業務線中如何配置使用 pnpm、lerna 以及需要注意的坑有哪些
感興趣的小夥伴趕緊收藏學習吧 ^_^
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場景下的耗時:
下面表格是上圖中的具體數據:
可以看到pnpm(橘色)有很明顯性能提升,在我們項目實踐中(基於gitlib)提升更明顯(跟store dir搭配使用)
在討論性能提升原因之前,我們先了解下現有包管理工具中node_modules存在的問題
1 node_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+結構差異:
包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是如何解決這種問題的呢?
2 pnpm 的破解之道:網狀 + 平鋪的 node_modules 結構
一些背景知識:inode、hardl link 和 symbolic link 的基礎概念 [3]
pnpm的用戶可能會發現它node_modules並不是扁平化結構,而是目錄樹的結構,類似npm version 2.x版本中的結構,如下圖所示
同時還有個.pnpm目錄,如下圖所示
.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 如下圖所示
pnpm install的安裝過程中,我們會看到如下的信息,這個裏面的Content-addressable store就是我們目前說的Store
CAS 內容尋址存儲,是一種存儲信息的方式,根據內容而不是位置進行檢索信息的存儲方式。
Virtual store 虛擬存儲,指向存儲的鏈接的目錄,所有直接和間接依賴項都鏈接到此目錄中,項目當中的. pnpm 目錄
如果是 npm 或 yarn,那麼這個依賴在多個項目中使用,在每次安裝的時候都會被重新下載一次
如圖可以看到在使用 pnpm 對項目安裝依賴的時候,如果某個依賴在 sotre 目錄中存在了話,那麼就會直接從 store 目錄裏面去 hard-link,避免了二次安裝帶來的時間消耗,如果依賴在 store 目錄裏面不存在的話,就會去下載一次。
當然這裏你可能也會有問題:如果安裝了很多很多不同的依賴,那麼 store 目錄會不會越來越大?
答案是當然會存在,針對這個問題,pnpm 提供了一個命令來解決這個問題: pnpm store | pnpm[5]。
同時該命令提供了一個選項,使用方法爲 pnpm store prune ,它提供了一種用於刪除一些不被全局項目所引用到的 packages 的功能,例如有個包 axios@1.0.0 被一個項目所引用了,但是某次修改使得項目裏這個包被更新到了 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,那麼這個依賴在多個項目中使用,在每次安裝的時候都會被重新下載一次。
如圖可以看到在使用 pnpm 對項目安裝依賴的時候,如果某個依賴在 sotre 目錄中存在了話,那麼就會直接從 store 目錄裏面去 hard-link,避免了二次安裝帶來的時間消耗,如果依賴在 store 目錄裏面不存在的話,就會去下載一次。
通過Store + hard link的方式,不僅解決了項目中的 NPM doppelgangers 問題,項目之間也不存在該問題,從而完美解決了npm3+和yarn中的包重複問題!
如果隨着項目越來越大,版本變更變多,歷史版本的資源會堆積,導致Store目錄越來越大,那如何解決這個問題呢?
針對這個現象,pnpm 提供了一個命令來解決這個問題: pnpm store | pnpm[7]。
同時該命令提供了一個選項,使用方法爲 pnpm store prune ,它提供了一種用於刪除一些不被全局項目所引用到的 packages 的功能,例如有個包 axios@1.0.0 被一個項目所引用了,但是某次修改使得項目裏這個包被更新到了 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 D4 個 repo。每個 repo 有各自的一些依賴項(包括 dependencies 和 peerDependencies),假定結構如下圖所示:(需要注意有個 peer dep)
下面是pnpm workspace中,比較清晰(不清晰的話留言,我可以改改!)說明了Store和Links間的相互關係:
官網也更新了類似的調用關 圖,大家也可以看看!
PeerDependencies
pnpm 的最佳特徵之一是,在一個項目中,package的一個特定版本將始終只有一組依賴項。 這個規則有一個例外 - 那就是具有 peer dependencies [9] 的package。
通常,如果一個package沒有 peer 依賴項(peer dependencies),它會被硬鏈接到其依賴項的軟連接(symlinks)旁的 node_modules,就像這樣:
如果 foo 有 peer 依賴(peer dependencies),那麼它可能就會有多組依賴項,所以我們爲不同的 peer 依賴項創建不同的解析:
pnpm創建foo@1.0.0_bar@1.0.0+baz@1.0.0 或foo@1.0.0_bar@1.0.0+baz@1.1.0內到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便可在項目中有幾組不同的依賴項。 例如,a@1.0.0具有單個依賴項b@1.0.0。b@1.0.0有一個 peer 依賴爲c@^1。a@1.0.0永遠不會解析b@1.0.0的 peer, 所以它也會依賴於b@1.0.0的 peer 。
如果需要解決 peerDep 引入的多實例問題,可以通過 .pnpmfile.cjs[10] 文件更改依賴項的依賴關係。
Part2 lerna
Lerna 是一個管理工具,用於管理包含多個軟件包(package)的 JavaScript 項目。它自身功能非常強大,特別是版本變更、項目發佈等功能,可以滿足各種複雜場景的訴求,除了workspace經常被人吐槽(可以使用yarn workspace),是業界當中使用規模最多的 repo 管理工具。
因爲項目中要使用import帶入、version版本變更、publish項目發佈功能,所以着重介紹這三個命令,想要了解更多的同學可以去官網查看
3 lerna import
將一個包內容(包括提交歷史記錄)導入到monorepo中。
命令如下:
lerna import <path-to-external-repository> --preserve-commit
<path-to-external-repository>是本地代碼的存儲目錄,執行後導入到packages/<directory-name>中,包括原始提交作者、日期和消息。執行前需要確保分支的正確性(一般是 master 分支),之後導入就會自動執行。
需要注意目前僅支持本地導入,遠程導入的話需要使用一些其他技巧。
--preserve-commit選項,使用該配置項可以保留原始提交者和提交日期,從而避免下面的問題。
每次 git 提交都有一位作者和一位提交者 (每人都有一個單獨的日期)。通常他們是同一個人 (和日期),但是因爲 lerna import 從外部存儲庫重新創建每個提交,提交者就變成了當前的 git 用戶 (和日期)。這在技術上是正確的,但邏輯上不對,例如,在 Github 上,如果作者和提交者是不同的人,它就會同時顯示他們,這可能會導致導入提交時的歷史 / 職責出現混亂。
import命令對歷史代碼遷移到 mono repo 倉庫特比有用。同時對每次歷史變更爲相對包目錄進行修改。例如,添加package.json的提交將改爲添加packages/<directory-name>/package.json。
4 lerna 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] 創建發佈。
5 lerna 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]
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 管理方案之一。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/pe3UZz_tWudIlAnWiA5yYg