跳到主要內容

即插即用

什麼是 Yarn 即插即用?

Yarn 即插即用(通常稱為 Yarn PnP)是 Yarn 最新版本中的預設安裝策略。它可以替換為更傳統的方法(包括 node_modules 安裝,或 pnpm 風格的基於符號連結的方法),但我們建議在建立新專案時使用它,因為它有許多改進。

它是如何運作的?

如果您查看專案中的檔案,您可能會注意到沒有 node_modules 資料夾。這不尋常!我們經常在 Discord 上被問到資料夾在哪裡,因為人們認為 yarn install 靜默失敗。

重點是,這其實是預期的!Yarn PnP 的運作方式是,它會指示 Yarn 產生一個單一的 Node.js 載入器檔案,取代典型的 node_modules 資料夾。這個載入器檔案名為 .pnp.cjs,它包含所有關於專案相依性樹狀結構的資訊,讓你的工具知道套件在磁碟上的位置,並讓它們知道如何解析 require 和 import 呼叫。

有什麼優點?

Yarn PnP 處理了各種問題。透過更聰明的 node_modules 佈局演算法,有可能處理其中一些問題(例如,基於符號連結的安裝策略 pnpm-style 所嘗試執行的),但 PnP 是唯一可以處理所有問題的策略

最小的安裝佔用空間

Yarn PnP 安裝通常只做一件事:產生 Node.js 載入器檔案(.pnp.cjs)。在其他套件管理員中,大部分時間都花在執行 I/O 作業,將檔案從一個位置複製到另一個位置,例如在磁碟上像 npm,或透過符號連結 / 硬連結像 pnpm。

跨磁碟的共用安裝

與前一點相關,Yarn PnP 允許在磁碟上的所有專案中重複使用相同的套件成品。與 pnpm 不同,pnpm 使用內容可尋址儲存,其中每個套件的每個檔案都需要硬連結到其最終目的地,PnP 載入器直接透過快取路徑參照套件,消除了許多複雜性。

完美且正確的提升

典型的 node_modules 安裝嘗試透過提升套件來最佳化產生的 node_modules 大小,但代價是提升幽靈相依性的風險。不幸的是,即使這些最佳化也有限制!有些相依性模式會阻止安全的提升,導致套件重複和多個實例化。

幽靈相依性防護

由於 Yarn 保留所有套件及其相依性的清單,因此它可以防止存取在解析期間未計入的相依性,讓你能夠在這些問題深入你的程式碼庫並在部署時危害應用程式的穩定性之前,快速找出並修復這些問題。

資訊

這有時會被提到為採用 Yarn PnP 的挑戰。這表示當其他套件管理員看似開箱即用時,可能會回報錯誤 - 也就是說,直到你新增、升級或移除不相關的相依項時,奇怪的故障才開始發生。

儘管它確實增加了一些摩擦,但它是 Yarn 成為一個非常穩定的套件管理員的關鍵部分。今天運作的應用程式未來不會突然中斷,而且你的同事在你的公關合併後很長一段時間內都不會遇到看似隨機的問題。

語意錯誤

你可能從未注意到,但是當 Node.js 匯入或 require 呼叫無效時,你只會收到一個一般性錯誤,它並未真正告訴你問題是什麼或如何解決它

Uncaught Error: Cannot find module 'not-found'

Yarn PnP 不僅會明確告訴你問題是什麼,還會告訴你哪些套件有問題。例如,根據情況可能會發出以下兩個錯誤訊息

Error: Your application tried to access not-found, but it isn't declared in your dependencies; this makes the require call ambiguous and unsound.

Required package: not-found
Required by: /path/to/my-project/
Error: awesome-plugin tried to access awesome-core (a peer dependency) but it isn't provided by its ancestors; this makes the require call ambiguous and unsound.

Required package: awesome-plugin
Required by: awesome-core

Ancestor breaking the chain: awesome-template

語意錯誤有助於讓你了解和解決由你的相依項所造成的問題。

它難以使用嗎?

在建立新專案時

如果你從頭開始建立專案,你的專案本身應該幾乎「開箱即用」。你可能必須不時使用 packageExtensions 來修復偶爾出現的幽靈相依項,但這並不常見,而且這個程序也很簡單。生態系統中的大多數工具都經過設計和測試,可以在 Yarn PnP 環境中順利運作,因此問題很少見。

危險

一個值得注意的例外是 React Native / Expo,它們需要使用典型的 node_modules 安裝。

實際上,你將面臨的主要問題將圍繞 IDE 整合。所有 IDE 都對 Yarn PnP 提供一定程度的支援,但一般來說,你應該預期必須遵循本指南中的其中一個程序,以確保所有匯入都正確解析。

當遷移現有專案時

資訊

在過去由 Yarn Classic 安裝的專案中執行 yarn install 會自動停用 Yarn PnP,以使遷移更順利。您仍能受惠於現代版本中實作的增強穩定性和其他功能,並可在稍後時間決定是否花時間遷移至 PnP。

現有專案可能較難遷移至 Yarn PnP,原因有幾個

  • 您已經開始使用許多相依性,因此可能會列出幽靈相依性的套件數量成正比增加
  • 它們可能鎖定在各自套件的舊版本上,因此包含幽靈相依性的機率較高
  • 您的自訂指令碼可能會無意間依賴某些實作詳細資料或幽靈相依性,有時甚至在您不知情的情況下。

這些都不是阻礙因素,但它們表示將現有專案遷移至 Yarn PnP 可能需要幾天時間。然而,我們提供工具來簡化此程序的一部分,而查看以下陷阱將有助於您更快找出導致某項功能中斷的原因,因此並非不可能。

請記住,遷移至 Yarn PnP 是可選的:您可以隨時透過在專案的 .yarnrc.yml 檔案中設定 nodeLinker: node-modules 設定,回復至 node_modules 安裝。

陷阱

同儕相依性

同儕相依性很強大,但實作起來非常困難,對於非 PnP 專案來說更是如此,因為它們必須在檔案系統階層允許的限制內運作。

另一方面,Yarn PnP 沒有此限制,且會準確表示相依性樹中每個專案的同儕相依性,甚至包括工作區。如果工作區具有同儕相依性,且此相依性由不同版本滿足,具體取決於其祖先,則工作區將實例化兩次,每個唯一的「相依性設定」一次。

這是正確的行為,但如果您的專案大量使用同儕相依性,且未確保它們始終由完全相同的版本滿足,則可能會導致實例化工作區的數量意外激增。

共用二進位檔

Yarn 可防止專案依賴套件中的幽靈依賴,以及您自己的程式碼本身,這是為了降低套件在開發機器上運作,但發佈後卻中斷的機率。

不過,當涉及到 bin 時,它會產生副作用。如果專案根目錄中列出了 typescript,則 tsc 二進位檔會在根目錄套件中可用,但僅限於根目錄專案。換句話說,任何在指令碼中使用 tsc 二進位檔的工作區都需要在依賴項中宣告它。

避免此類問題的良好建議是建立一個「工具」工作區,以包含您的基礎架構工具和指令碼,並讓所有其他工作區依賴它。

常見問題

與 npm / pnpm 相容性

Yarn PnP 設計為使用與其他套件管理員完全相同的「公開介面」,差異僅限於實作細節。如果專案可以使用 Yarn PnP,它應該可以在任何地方使用!

不過,有一個警告:反之不一定成立。由於其他套件管理員不會/無法強制正確列出依賴項,因此它們更容易意外將幽靈依賴項傳送給消費者。如此一來,使用 Yarn PnP 可以視為維護生態系統健康的良好實務! 🙂

我如何修復幽靈依賴項?

可以使用 packageExtensions 設定來解決幽靈依賴項,它允許您將新的依賴項新增到依賴項樹中的任何套件。例如,如果您遇到錯誤,例如 @babel/core 嘗試存取 @babel/types,但它未在依賴項中宣告,您可以透過將下列內容新增到 .yarnrc.yml 檔案中來輕鬆修復它

packageExtensions:
"@babel/core@*":
dependencies:
"@babel/types": "*"

有時延伸 peerDependencies 欄位比延伸 dependencies 欄位更有意義,這必須視情況而定。

提示

為了避免您必須新增太多 packageExtensions 項目,Yarn 團隊會維護一份 生態系統中已知的幽靈相依項清單,我們會自動修正。此清單會同時被 Yarn 和 pnpm 使用,我們非常樂意合併貢獻。