為什麼我們從Yarn切換到pnpm

luckrain7發表於2020-07-08

logo.png

原文網址:https://www.takeshape.io/arti...

原文作者:ANDREW SPROUSE

這是一個重大的決定

在 TakeShape,我們非常關注開發人員的生產力。 我們是一個資源有限的小型團隊,因此值得花時間考慮如何更快,更高效地合作。 在最近重構我們的構建過程時,我們做出了一個重大決定:我們將拋棄 Yarn 並改用 pnpm 來管理我們的依賴項並執行我們的指令碼。 這是關於我們如何做出該決定以及迄今為止如何使我們受益的故事。

pnpm.png

最初,TakeShape 的程式碼庫分散在多個Git儲存庫中。 每個軟體包都是獨立開發的,並且彼此依賴。 從理論上講,這是理想的設定。 在實踐中,我們發現所有東西都是相互依賴的,我們真的希望能夠同時測試和釋出所有軟體包。 當我們為其中一個軟體包發行新版本時會遇到失敗,但是會忘記在依賴它的其他專案中更新該版本。

最終,我們意識到,在保持專案的分離性和依賴性時,monorepo 是正確的權衡。 我們所有的軟體包(如Web客戶端,前端路由庫和CLI)都存在一個可測試且可部署的單元中。 我們的包可以使用package.json 中的 link :語法相互依賴。

這在很大程度上是有效的,但是我們仍然發現管理我們的 monorepo 的部分是乏味的。這在一定程度上是因為我們的 monorepo 中的每個包都用自己的包管理器管理自己的依賴關係,json和它自己的lock 檔案。即使每個包使用相同的開發工具鏈,如eslint、Jest、Typescript和Babel,每個包單獨宣告這些 devdependency ,這個工具鏈必須在我們所有的包中保持最新。我們決定不使用Yarn的工作區特性來解決這個問題,因為這將需要拋棄每個包的lock檔案,而使用單個工作區範圍的lock檔案。

避免幻像依賴也比實際需要更加棘手。 當您的程式碼匯入未在 package.json 中宣告的包時,就會產生幻像依賴。 假設您將 Package A 新增到依賴於 Package B 的專案中。由於 Yarn 將所有程式包都保留在 node_modules 的根目錄下,因此您可以匯入和使用 Package B ,而無需將其完全放在 package.json 中。 儘管不是很常見,但這是一個錯誤的做法,它確實會減慢除錯過程的速度,除非您記得有意地檢查它。

我們的 monorepo 也使我們的CI管道比所需的更加複雜。 首先,我們並行化了 CircleCI 構建,以加快慢速 Webpack 構建。 但是,隨著我們的 monorepo 的增長,為每個構建分別安裝依賴項的開銷也在增加。 為每個構建安裝依賴項成為瓶頸。 作為回應,我們使用自己編寫和維護的 CircleCI 指令碼鞏固了構建過程,以減少使用工作。最終,我們得到了一組脆弱的CI指令碼來對任何包進行剪裁、測試和構建更改。

2.png

我們真正需要的是一種遞迴安裝軟體包,提升所有共享依賴關係並執行指令碼以進行lint,測試和構建的方法。 我們知道Yarn並不能為我們擴充套件,因此我們開始考慮替代方案:

  • 如果我們保留Yarn並新增Lerna怎麼辦? 新增Lerna可以解決CI指令碼編寫中的一些問題,但不能解決幻像依賴項或重複的devDependencies帶來的問題。
  • 如果我們新增了Lerna並使用了Yarn Workspaces,該怎麼辦? 工作區將解決共享開發人員依賴關係並自動連結依賴關係的問題。 但是將所有node_modules提升到專案根目錄只會加劇幻想依賴項的風險,並導致某些模組和工具出現問題。
  • 如果我們升級到Yarn 2.0並使用……其他……該怎麼辦? Yarn 2.0確實令人興奮。 它具有很多很酷的功能,包括 Plug'n'Play(PnP)。 PnP可以解決幻想依賴項的問題,但是它可能與某些需要檔案訪問的依賴項不相容。 Yarn 2.0與Lerna不相容; 相反,它具有外掛架構。 這些外掛有可能解決我們對CI指令碼的需求,但它們還不夠成熟,無法自信地用於生產中。
  • 如果我們用pnpm替換Yarn怎麼辦? 根據 pnpm 的說法,它存在“ [使用]硬連結和符號連結僅一次在磁碟上儲存模組的一個版本”。 這種組織 node_modules 的方法可以防止幻象依賴並避免在吊裝時出現其他問題。 它可以解決與Yarn 2.0的PnP相同的問題,但是由於它僅使用連結,因此具有更廣泛的相容性。 pnpm還包含與Lerna類似的過濾功能。

最後,pnpm對我們來說最有意義。 我們發現pnpm的遞迴命令和--filter標誌消除了我們對像Lerna這樣的單獨軟體包的需要。 將Babel,ESLint和Jest之類的共享開發人員依賴項無縫移動到我們專案的頂層,現在可以從單個共享源更新這些軟體包。 在切換過程中,我們甚至在我們的專案中發現並修復了多個幻象依賴項!

採用pnpm後,我們複雜的CircleCI管道被簡化為一個作業。結果,我們將整個可計費的CircleCI分鐘減少了大約一半!我們認為通過調整設定可以獲得更多的好處,但這是一個很好的開始。

當然,每個決策都需要權衡利弊,pnpm也不例外。雖然pnpm由zkochan積極維護,但與Yarn或NPM相比,它是一個不太受歡迎的專案。雖然微軟使用的是PNPM,但它沒有像Yarn從Facebook獲得的直接企業贊助那麼高。和pnpm有自己的lockfile格式,所以它不直接相容Yarn或NPM。我們很難知道未來會發生什麼,但如果我們需要從pnpm中脫離出來,這將是一個比我們希望的更大的任務。

儘管聽起來很傻,但Yarn不需要自定義指令碼的“執行”命令,這使我們寵壞了,因此我們不得不重新訓練我們的肌肉記憶。 這是一個很小的折衷,但它確實增加了我們的團隊切換這樣一個基本的日常工作流程的成本。

但是,歸根結底,這些成本遠遠超過了pnpm對我們的堆疊所做出的改進。 現在,我們可以比以往更快,更有效地進行工作,並且需要付出的代價更少。

pnpm可能不是適用於每個專案或每個堆疊的正確工具,但是如果您遇到與我們的monorepo相同的任何問題,請看看並考慮將其作為替代方法。

相關文章