跳至主要內容

問題與解答

npm 上的 yarn 套件為何仍停留在 1.x?

Yarn 的 Modern 版本自 2019 年起就不再於 npm 上發布。

原因很簡單:由於 Yarn 並未與 Node.js 一併發布,許多人仰賴 npm install -g yarn 等方式作為映像建置的一部分。這表示任何重大變更都會影響所有使用此模式的人,並導致他們的部署中斷。

因此,我們決定停用 yarn npm 套件,並僅將其用於所需的幾個 1.x 維護版本。Yarn 現在可透過 Corepackyarn set version 從我們的網站直接安裝。

您為什麼應該升級到 Yarn Modern?

雖然 Yarn Classic 系列 (1.x) 仍然是 JavaScript 生態系統的支柱,但我們建議您在可能的情況下升級。為什麼呢?

  1. 新功能:除了您已經習慣的經典功能之外,您還將發現新的功能(yarn dlx內建 patch: 協定,...),Modern 提供擴充功能,可透過 變更集限制工作區 等擴充 Yarn 的功能集。

  2. 效率:Modern 具備新的安裝策略,讓專案僅佔過去的一小部分;例如,在預設設定下,庫存 CRA 製成品現在僅佔用 45MB,而非 237MB。效能 也獲得改善,現在大多數安裝即使在極大型專案上也只需花費幾秒鐘。我們甚至讓您能夠達到 零秒

  3. 可擴充性:Modern 的架構讓您可以在需要時建立自己的功能。您不再需要等待我們實作您夢寐以求的功能,現在您可以根據自己的規格自行實作!專注的工作區、自訂安裝、專案驗證,...

  4. 穩定性:Modern 歷經多年維護 Classic 的經驗而來;它讓我們終於能夠修正某些功能實作方式的長期設計問題。工作區現在是核心元件,解析管線已經簡化,資料結構更有效率...因此,Modern 不太可能受到不正確的假設和其他設計缺陷的影響。

  5. 未來證明:我們投資 Modern 的一大原因在於,我們注意到在 Classic 上建立新功能變得困難 - 每項變更都很有可能產生無法預見的後果。Modern 架構從我們的錯誤中吸取教訓,並設計成讓我們能以更快的速度建立功能 - 我們的速度提升就是證明。

您預期從 Classic 遷移到 Modern 的難度為何?

一般來說,需要處理幾項主要事項

  1. 設定格式已變更。我們不再讀取 .npmrc.yarnrc 檔案,而是從 .yarnrc.yml 檔案 取得設定。

  2. 有些第三方套件未正確列出其相依性,需要透過 packageExtensions 設定來協助。

  3. 文字編輯器的支援相當完善,但您需要執行 SDK 文件 中列出的單次設定。

  4. 有些工具(主要是 React Native 和 Flow)需要透過將 nodeLinker 設定設為 node-modules,來降級到 node_modules 安裝策略。TypeScript 沒有這個問題。

大多數專案只會遇到這四個問題,這些問題都可以在一個下午的工作時間內修復。如需更詳細的說明,請參閱詳細的 遷移指南

哪些檔案應該 gitignore?

如果您使用 Zero-Installs

.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

如果您未使用 Zero-Installs

.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

如果您有興趣進一步了解這些檔案

  • .yarn/cache.pnp.* 可以安全地忽略,但您需要執行 yarn install 來在每次切換分支時重新產生它們 - 否則這是可選的,請參閱 Zero-Installs

  • .yarn/install-state.gz 是您永遠不應該提交的最佳化檔案。它僅儲存專案的確切狀態,以便下一個指令可以啟動,而無需再次解析您的工作空間。

  • .yarn/patches 包含使用 yarn patch-commit 指令產生的修補檔檔案。由於它們對於安裝相依項是必要的,因此您總是希望它們在儲存庫中。

  • .yarn/plugins.yarn/releases 包含目前儲存庫中使用的 Yarn 版本(由 yarn set version 定義)。您會希望讓它們保持版本化(如果,比方說,兩個工程師使用具有不同功能的不同 Yarn 版本,這可以防止潛在問題)。

  • .yarn/sdks 包含由 @yarnpkg/sdks 產生的編輯器 SDK。是否將它保留在您的儲存庫中由您決定;如果您不保留,您將需要在新複製中再次遵循編輯器程序。有關更多詳細資訊,請參閱 編輯器 SDK

  • .yarn/unplugged 通常應始終被忽略,因為它們通常包含特定於電腦的建置成品。但是,忽略它可能會阻止 零安裝 運作(為防止此情況,請將 enableScripts 設為 false)。

  • .yarn/versions版本外掛程式 用於儲存套件版本定義。您會希望將它保留在您的儲存庫中。

  • yarn.lock 應始終儲存在您的儲存庫中(即使您開發的是函式庫)。

  • .yarnrc.yml(及其較舊的對應版本 .yarnrc)是組態檔。它們應始終儲存在您的專案中。

提示:您也可以新增一個 .gitattributes 檔,以將版本和外掛程式套件識別為二進位內容。這樣一來,每次您新增或更新它們時,Git 就不會費心顯示大量的差異。

/.yarn/releases/** binary
/.yarn/plugins/** binary

Yarn 是否支援 ESM?

是的。

首先,請記住 Yarn 支援 node-modules 安裝策略,其安裝套件的方式與,比方說,npm 完全相同。因此,如果 Yarn 不支援 ESM,其他工具也不會支援。如果您聽說有人說它不支援,他們實際上是指「Yarn PnP 不支援 ESM」 - 但它支援,自 3.1 以來一直如此。

因此,這本身就能回答你的問題。但如果你想要更多關於 PnP 和 ESM 故事的詳細資訊,那麼讓我們先來談談 ESM 本身。ESM 有兩個面向:其核心是一個在 ES2015 中起草的規範。然而,沒有任何引擎立即實作它:此時,該規範幾乎只是一個語法佔位符,沒有任何具體的基礎。直到 2019 年底,Node 才終於獲得對原生 ESM 的支援,而不需要實驗性標記。但這個支援有一個主要的警告:ESM 載入器不存在。載入器允許專案告訴 Node 如何在磁碟上找到套件和模組。你可能知道其中一些:@babel/registerts-nodeJest 的模擬Electron,以及更多。

與 CommonJS 不同,ESM 模組解析流程旨在完全與外部隔離,例如允許多個執行緒共用同一個解析器實例。除此之外,這意味著,在沒有官方載入器支援的情況下,支援備用解析策略是不可能的 - 修改解析原語不再可行,因此所有這些專案根本無法支援 ESM。這只意味著一件事:ESM 尚未準備好。是的,它獲得原生支援,但由於它在沒有任何替代方案的情況下破壞了生態系統的相當大一部分,因此它還不能成為合理的標準。

幸運的是,Node 發現了這個問題,開始開發載入器,並發布了第一個版本。快速轉到今天,Node 載入器仍然在大量工作中(並且形狀改變了不止一次,正如這個「實驗性」註解所強調的),但已允許我們起草 ESM 相容 PnP 載入器的第一個實作,我們在 3.1 中發布了它。憑藉這些學習,我們開始為 Node 載入器工作小組做出貢獻,不僅幫助 Yarn 自己的用例,也幫助其他可能追隨我們領導的專案。

載入器還不完美,在它們完美之前,僅限 ESM 的套件不建議使用,但有一個前進的方向,隨著我們共同努力,我們將會到達那裡。我們只需要小心不要在我們朝著目標前進時將人們推開。

是否應將鎖定檔提交至儲存庫?

是的。

鎖定檔應始終與專案來源一起儲存,無論您撰寫的是獨立應用程式或分散式函式庫。

反對在儲存庫中檢查鎖定檔的一個持續論點是,需要了解針對函式庫最新版本的潛在問題。持此論點的人認為,鎖定檔的存在會阻止貢獻者看到這些問題,因為所有依賴關係都已鎖定,且在使用者安裝函式庫並使用較新的(且不相容的)依賴關係之前,看起來都很好。

儘管誘人,但此論點有一個致命的缺陷:從儲存庫中移除鎖定檔並不能防止此問題發生。特別是

  • 活躍的貢獻者不會取得新版本,除非他們明確移除其安裝成品(node_modules),這可能不會經常發生。因此,有問題的依賴關係升級將主要由新貢獻者發現,這並非良好的首次體驗,且可能會阻止貢獻。

  • 即使假設您每週執行全新安裝,您的升級也不容易還原,一旦您測試最新套件,您就不會測試較舊的套件。相容性問題仍然存在,它們只是針對以前可行但您不再測試的套件。換句話說,透過始終測試最新的 semver 版本,您不會發現自己是否意外開始依賴之前不可用的功能。

當然,這些重點只是一個問題的一部分,缺乏鎖定檔也表示儲存庫中缺少關鍵狀態資訊。幾個月後,當您或您的貢獻者想要修復舊專案之一時,您甚至可能無法再建立它,更不用說改進它了。

鎖定檔應始終保留在儲存庫中。持續整合測試是個好主意,但應留給持續整合系統。例如,Yarn 本身會針對主要開源架構和工具的最新版本執行每日測試,這讓我們可以快速找出與最新版本的任何相容性問題,同時仍保證每個貢獻者都能獲得與專案合作的一致體驗。DependabotRenovate 也是不錯的工具,可為您追蹤依賴關係更新。

如何在工作空間之間共用指令碼?

鮮為人知的 Yarn 功能:任何名稱中帶有冒號的指令碼(build:foo)都可以從任何工作空間呼叫。另一個鮮為人知的特色:$INIT_CWD 將始終指向執行指令碼的目錄。綜合起來,您可以撰寫可以這樣重複使用的指令碼

{
"dependencies": {
"typescript": "^3.8.0"
},
"scripts": {
"g:tsc": "cd $INIT_CWD && tsc"
}
}

然後,從任何包含其自己的 tsconfig.json 的工作區,你將能夠呼叫 TypeScript

{
"scripts": {
"build": "yarn g:tsc"
}
}

或如果你只想從根工作區使用 tsc

{
"scripts": {
"build": "run -T tsc"
}
}

如果你想在專案的基礎上執行指令碼

{
"scripts": {
"build": "node ${PROJECT_CWD}/scripts/update-contributors.js"
}
}

Yarn 是由 Facebook 營運的嗎?

否。

儘管 Yarn 的第一個版本是由 Sebastian McKenzie 在 Facebook 工作時實作的,但初始設計收到了來自其他各種公司的回饋(例如 Tilde 透過 Yehuda Katz),而且專案被放入其自己的 GitHub 組織 中。Facebook 在接下來的幾年持續投資於它(主要是因為它被證明是 RN 生態系統的關鍵部分),但主要貢獻也來自於開放原始碼。

現在,積極的開發團隊完全由非創辦公司僱用的員工組成。當然,Facebook 員工仍然歡迎對專案提供貢獻,但他們將與其他人經歷相同的審查流程。

為什麼是 registry.yarnpkg.com?Facebook 會追蹤我們嗎?

否。

當 Yarn 被建立時,npm 註冊表透過 Fastly 提供服務。這顯然影響了安裝效能,因此最初的團隊決定與 Cloudflare 合作,並設定一個 反向代理伺服器,它會在傳回請求之前簡單地更好地快取請求。這個設定甚至在我們這邊沒有後端。

在某個時間點,npm 也切換到 Cloudflare,而我們關閉了代理伺服器,並用 CNAME (證明) 取代它。我們仍然出於可靠性原因保留主機名稱 - 雖然有理由認為 Yarn 網域名稱會在 Yarn 被使用的時間內持續維護,但 npm 網域名稱不一定也是如此。如果主要來源無法使用,這使我們能夠重新導向到註冊表的唯讀副本。

雖然我們確實會收集一些基本的 用戶端遙測,但沒有任何 http 記錄可以到達 Yarn 專案基礎架構 - 甚至更少到達 Facebook,它無法控制專案(另請參閱 Yarn 是由 Facebook 營運的嗎?)。

registry.yarnpkg.com 的查詢傳回 404/500/...;它當機了嗎?

否。

前一節 中所述,Yarn 註冊表只是一個指向 npm 註冊表的 CNAME。由於我們甚至沒有後端,因此任何伺服器錯誤都只能來自 npm 註冊表,因此應向他們報告並在他們的 狀態頁面 上監控。

Yarn 是否比其他套件管理員快?

聳肩 🤷‍♀️

在 Yarn 發布時,Yarn 確實比一些競爭對手快很多。遺憾的是,我們未能強調效能並非我們持續開發 Yarn 的主要原因。效能會隨著時間而變化,因此儘管我們非常快,但這並非因為我們做了什麼了不起的事,而是因為競爭對手的實作有嚴重的錯誤。當該錯誤修復後,我們的錯誤溝通變得更加明顯,因為有些人認為 Yarn 就是關於效能。

簡單來說,我們的差異在於優先順序。不同的專案有不同的取捨,這正是這裡發生的事。我們優先考慮工作空間,因為我們覺得單一儲存庫提供了顯著的價值。我們投入了大量的資源推動 Plug'n'Play(包括透過 對第三方專案的數十項貢獻),因為我們覺得這對生態系統很重要。這是主要的差異:我們針對專案路線圖做出自己的明智決策。

速度是相對的,也是暫時的狀態。流程、路線圖和核心價值觀才是持久的。

即使我不使用 Plug'n'Play,為什麼 TypeScript 仍會修補?

由於 PnP 是不同於 Node 的解析器標準,因此重新實作 require.resolve API 的工具需要新增一些邏輯來考量 PnP 解析。雖然各種專案都這樣做了(例如 Webpack 5 現在開箱即用地支援 PnP),但少數專案仍對此猶豫不決。在 TypeScript 的情況下,我們開始並持續維護一個 pull request,但 TypeScript 團隊仍必須接受它。為了解除使用者的封鎖,我們決定使用我們新的 patch: 協定,自動將這個確切的 pull request 套用至下載的 TypeScript 版本。

現在引發了一個問題:即使停用 Plug'n'Play,我們為什麼仍套用這個修補程式?主要原因是 Yarn 打算提供一致的行為。有些設定會在開發期間使用 node_modules 連結器(以避免必須設定編輯器 SDK),並在生產環境中使用 PnP(以加快安裝速度)。如果我們只在啟用 PnP 時套用修補程式,則套件快取會變成不同,例如會中斷不可變安裝。

我們可以透過開關讓它可設定,但最後我們決定不值得額外的設定

  • 如果 PnP 未啟用,TypeScript 修補程式為 noop,因此這不應影響你的工作(如果確實會,請開啟問題)
  • 我們希望最終有一天能將此 PR 登陸到 TypeScript,因此我們能獲得的關注越多,我們的信心就越高
  • 自 Yarn 3+ 起,失敗的內建修補程式將會被忽略,並回退到原始來源