基于pnpm + lerna + typescript的最佳项目实践 - 理论篇
本文來自作者@金虹橋程序員 投稿
原文鏈接:https://juejin.cn/post/7043998041786810398
本系列文章分為兩篇:理論篇和實踐篇
理論篇:介紹pnpm(pnpm的特點、解決的問題等)、lerna(lerna的常用命令)、typescript
實踐篇:業務線中如何配置使用pnpm、lerna以及需要注意的坑有哪些
感興趣的小伙伴趕緊收藏學習吧 ?^_^
Part1pnpm
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下面表格是上圖中的具體數據:
| 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的結構)。
image.png至于哪個版本的包被提升,依賴于包的安裝順序!
依賴變更會影響提升的版本號,比如變更后,有可能是B 1.0 ,也有可能是 B 2.0被提升上來(但只能有一個版本提升)
細心的小伙伴可能發現,這其實并沒有解決之前的問題,反而又引入了新的問題
image.pngnpm3+和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是如何解決這種問題的呢?
image.png2pnpm的破解之道:網狀 + 平鋪的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?directoryreturn?path.join(homedir,?relStore,?STORE_VERSION) }由于每個磁盤有自己的存儲方式,所以Store會根據磁盤來劃分。 如果磁盤上存在主目錄,存儲則會被創建在?<home dir>/.pnpm-store;如果磁盤上沒有主目錄,那么將在文件系統的根目錄中創建該存儲。 例如,如果安裝發生在掛載在?/mnt?的文件系統上,那么存儲將在?/mnt/.pnpm-store?處創建。 Windows系統上也是如此。
可以在不同的磁盤上設置同一個存儲,但在這種情況下,pnpm 將復制包而不是硬鏈接它們,因為硬鏈接只能發生在同一文件系統同一分區上。
windows store如下圖所示
image.pngpnpm install的安裝過程中,我們會看到如下的信息,這個里面的Content-addressable store就是我們目前說的Store
image.pngCAS 內容尋址存儲,是一種存儲信息的方式,根據內容而不是位置進行檢索信息的存儲方式。
Virtual store 虛擬存儲,指向存儲的鏈接的目錄,所有直接和間接依賴項都鏈接到此目錄中,項目當中的.pnpm目錄
image.png如果是 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,那么這個依賴在多個項目中使用,在每次安裝的時候都會被重新下載一次。
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 的功能,例如有個包?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)
image.png下面是pnpm workspace中,比較清晰(不清晰的話留言,我可以改改!)說明了Store和Links間的相互關系:
image.png官網也更新了類似的調用關 圖,大家也可以看看!
image.pngPeerDependencies
pnpm 的最佳特征之一是,在一個項目中,package的一個特定版本將始終只有一組依賴項。 這個規則有一個例外 -那就是具有?peer dependencies?[9]的package。
通常,如果一個package沒有 peer 依賴項(peer dependencies),它會被硬鏈接到其依賴項的軟連接(symlinks)旁的?node_modules,就像這樣:
image.png如果?foo?有 peer 依賴(peer dependencies),那么它可能就會有多組依賴項,所以我們為不同的 peer 依賴項創建不同的解析:
image.pngpnpm創建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]文件更改依賴項的依賴關系。
Part2lerna
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]目前使用的模式。如果您想自動將所有軟件包版本綁定在一起,請使用此選項。
這種方法的存在兩個問題:
任何包的重大更改都會導致所有包都具有新的主要版本。
項目中包的版本變更會非???/p>
這些都是一致性帶來的衍生效應,具體大家可以自行評估
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] |
Part3typescript
TypeScript是JavaScript類型的超集,他可以編譯成純JavaScript。
TypeScript可以在任何瀏覽器、任何計算機和任何操作系統上運行,并且是開源的。
TS介紹的人已經相當多了,這里就不再介紹了!強力安利一波,用過的人都說香!
Part4總結
pnpm將來會成為主流的registry管理工具,這個毋庸置疑?,F在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管理方案之一。
參考資料點擊原文查看
·················?若川簡介?·················
你好,我是若川,畢業于江西高?!,F在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。
識別上方二維碼加我微信、拉你進源碼共讀群
今日話題
略。分享、收藏、點贊、在看我的文章就是對我最大的支持~
總結
以上是生活随笔為你收集整理的基于pnpm + lerna + typescript的最佳项目实践 - 理论篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(2967):实现路由跳转的方式
- 下一篇: PS教程第十课:像素计算