標籤:

Facebook 發布了新的 Node 模塊管理器 Yarn,或將取代 npm 客戶端

  • 原文鏈接 : Yarn: A new package manager for JavaScript
  • 原文作者 : SEBASTIAN MCKENZIE,CHRISTOPH POJER,JAMES KYLE

  • 譯文出自 : 掘金翻譯計劃 (翻譯不易,歡迎 Star 支持)

  • 譯者 : 達仔

  • 校對者: 根號三

在 JavaScript 社區中,工程師們互相分享成千上萬的代碼,幫助我們節省大量編寫基礎組件、類庫或框架的時間。每個代碼包可能都依賴於其他代碼,而代碼間的依賴關係則由包管理器負責維護。目前最流行的 JavaScript 包管理器是 npm 客戶端,在 npm 倉庫中提供了多達 30 萬的軟體包。據統計,已有超過 500 萬的工程師使用 npm 倉庫,其軟體包下載量達到了 50 億次/月。

在 Facebook 中,我們多年來一直在使用 npm 客戶端並取得了成功,但隨著代碼倉庫與團隊人數的增長,我們在一致性、安全性以及性能方面遇到了挑戰。在嘗試解決每個方面的問題後,我們最終決定著手打造一套新的客戶端解決方案,以幫助我們更可靠地管理依賴。我們把這個客戶端工具稱為 Yarn —— 更加快速、可靠、安全的 npm 客戶端的替代品。

我們在此榮幸地宣布,我們與 Exponent、 Google 和 Tilde 進行了合作,並開源 Yarn 項目。工程師在使用 Yarn 時,依然需要訪問 npm 倉庫,但 Yarn 能夠更快速地安裝軟體包和管理依賴關係,並且可以在跨機器或者無網路的安全環境中保持代碼的一致性。Yarn 提高了開發效率,並解決了共享代碼時面臨的一些問題,使得工程師們可以專註在構建新產品以及新特性上。

JavaScript 包管理方式在 Facebook 的演變

在包管理工具出現之前,JavaScript 工程師們通常依賴的項目並不多,因此會把依賴直接存儲在工程目錄或上傳到 CDN 上。在 Node.js 出現後不久,第一個主流的 JavaScript 包管理工具 npm 被引入進來,並很快成為了最受歡迎的包管理工具之一。從此,新的開源項目不斷湧現,工程師們比起以前更加樂於分享代碼了。

在 Facebook 中,我們有很多項目都要依賴 npm 倉庫上的代碼,比如 React。但隨著內部規模的擴大,我們面臨著以下挑戰:在跨平台與跨用戶之間安裝依賴時的代碼一致性問題、在安裝依賴時花費太長時間、以及 npm 客戶端自動執行某些依賴庫的代碼所導致的安全性問題。我們嘗試過尋找這些問題的解決方案,但在這個過程中通常又會引起一些新的問題。

嘗試修改 npm 客戶端

在開始階段,我們遵循了最佳實踐,在代碼倉庫中只跟蹤了 package.json 文件的變化,並要求工程師手動運行 npm install 命令安裝依賴。這種模式在開發人員的電腦上沒有問題,但在持續集成環境中遇到了困難,因為出於安全與可靠性的考慮,持續集成環境需要進行沙箱隔離,不能進行聯網,因此也無法安裝依賴。

接下來,我們嘗試在代碼倉庫中跟蹤整個 node_modules 目錄的文件變化。雖然這種方式有效,卻使得一些簡單操作變得複雜化了。比如,對 babel 更新一個次要版本號時,會產生多達 800,000 行的提交記錄,此外由於 lint 規則的存在,引起無效的 utf-8 位元組序列、windows 換行符、非 png 壓縮圖片等問題時,將會導致工程師經常需要花費一整天的時間合併 node_modules 目錄的文件。而我們負責源碼控制的團隊也指出,跟蹤 node_modules 目錄會引入過多的元數據。比如 React Native 的 package.json 文件目前只列出了68項依賴,但在運行 npm install 後,node_modules 目錄整整包含了 121,358 個文件。

最後,為了有效組織 Facebook 逐漸增長的工程師人數以及管理需要安裝的代碼量,我們嘗試修改 npm 客戶端。我們決定壓縮整個 node_modules 目錄,並上傳到內部 CDN,然後我們的工程師與持續集成系統都能從 CDN 上下載並解壓文件,從而保證了代碼一致性。這樣我們就可以從源碼控制系統中刪除數以萬計的文件了,但不足之處是工程師現在不僅在拉代碼時需要聯網了,構建也同樣需要聯網。

我們還試圖為 npm 的 shrinkwrap 功能尋求優化方案,這個工具是用來鎖定依賴版本號的。但 Shrinkwrap 功能的文件默認不會生成,如果開發者忘記了生成這一步驟,文件就不會被同步更新,因此我們編寫了一個工具,以確定 Shrinkwrap 的文件內容和 node_modules 目錄中的文件相符。這些文件由大量的 JSON 塊組成,並且鍵名是無序的,因此每次更改通常會導致 Shrinkwrap 文件的內容大幅變化,難以進行代碼審查。為減緩這一問題,我們還需要藉助一個額外的腳本,對所有條目進行排序。

最後,通過 npm 升級單個依賴包時,基於 語義化版本號 規則,npm 通常會連同其他無關依賴一起更新。這使得每次更新都會比預期產生更多的變化,工程師們認為這樣把 node_modules 提交上傳到 CDN 的過程,難以達到預期的效果。

構建新客戶端

與其圍繞 npm 客戶端繼續構建基礎設施,不如從整體上再次回顧這些問題。倫敦辦公室的 Sebastian McKenzie 提出,如果我們建立一個新客戶端工具以代替 npm 客戶端,從而解決我們的核心問題呢?這一構思很快得到了我們的認同,團隊對於這個主意也感到非常興奮。

在開發過程中,我們與業界的工程師們進行了交流討論,發現他們也面臨著類似的問題,也嘗試過許多類似的解決方案,通常只能把這些問題逐一解決。很明顯,有必要把整個 JavaScript 社區正在面臨的問題集合起來,然後我們就可以開發一個主流的解決方案了。在此感謝 Exponent、 Google 與 Tilde 的工程師們的協助,我們共同建立了 Yarn 客戶端,並在每一個主流 JS 框架以及 Facebook 外的使用場景中測試驗證了 Yarn 的性能。今天(2016-10-11),我們很榮幸把這個工具開源分享到社區中。

介紹 Yarn

Yarn 是一個新的包管理器,用於替代現有的 npm 客戶端或者其他兼容 npm 倉庫的包管理工具。Yarn 保留了現有工作流的特性,優點是更快、更安全、更可靠。

任何包管理器的主要功能都是安裝某些軟體包,軟體包即用於特定功能的某段代碼,通常是從一個全局的倉庫安裝到工程師的本地環境。每個軟體包可以依賴於其他包,也可以不依賴。一個典型的項目結構的依賴樹通常會包含數十個、數百個甚至上千個軟體包。

這些依賴包通常是帶版本號的,通過語義化版本控制(semver)安裝。Semver 定義的版本號反映了每個新版本更改的類型,到底是進行了不兼容的API改動(MAJOR),還是添加了向後兼容的新特性(MINOR),還是進行了向後兼容的 bug 修復(PATCH)。然而,semver 依賴於軟體包的開發者不能犯錯誤——如果依賴關係沒有加鎖,可能會引入一些破壞性更改或者產生新的 bug。

結構

在 Node 生態系統中,依賴通常安裝在項目的 node_modules 文件夾中。然而,這個文件的結構和實際依賴樹可能有所區別,因為重複的依賴可以合併到一起。npm 客戶端把依賴安裝到 node_modules 目錄的過程具有不確定性。這意味著當依賴的安裝順序不同時,node_modules 目錄的結構可能會發生變化。這種差異可能會導致類似「我的機子上可以運行,別的機子不行」的情況,並且通常要花費大量時間定位與解決。

Yarn 通過 lockfiles 文件以及一個確定性的、可靠的安裝演算法,解決了版本問題和 npm 的不確定性問題。Lockfile 文件把安裝的軟體包版本鎖定在某個特定版本,並保證 node_modules 目錄在所有機器上的安裝結果都是相同的。Lockfile 還使用簡潔的有序鍵名的格式,保證了每次的文件變化最小化,進行代碼審查也更為簡單。

安裝過程分為以下三個步驟:

  1. 處理: Yarn 通過向代碼倉庫發送請求,並遞歸查找每個依賴項,從而解決依賴關係。

  2. 抓取: 接下來,Yarn 會查找全局的緩存目錄,檢查所需的軟體包是否已被下載。如果沒有,Yarn 會抓取對應的壓縮包,並放置在全局的緩存目錄中,因此 Yarn 支持離線安裝,同一個安裝包不需要下載多次。依賴也可以通過 tarball 的壓縮形式放置在源碼控制系統中,以支持完整的離線安裝。

  3. 生成: 最後,Yarn 從全局緩存中把需要用到的所有文件複製到本地的 node_modules 目錄中。

通過清晰地細分這些步驟,以及確定性的演算法支持,使得 Yarn 支持並行操作,從而最大化地利用資源,並加速安裝進程。在一些 Facebook 的項目上,Yarn 甚至可以把安裝過程降低一個數量級,從幾分鐘到只需幾秒鐘。Yarn 還使用了互斥鎖,以確保多個 CLI 實例同時運行時不會互相衝突與影響。

縱觀整個過程,Yarn 對於軟體包安裝加上了嚴格的限制。你可以對哪個生命周期腳本作用於哪個軟體包進行控制。軟體包的 checksum 也會存儲在 lockfile 中,以確保每一次安裝都可以得到同一個包。

特性

Yarn 除了讓安裝過程變得更快與更可靠,還添加了一些額外的特性,從而進一步簡化依賴管理的工作流。

  • 同時兼容 npm 與 bower 工作流,並支持兩種軟體倉庫混合使用

  • 可以限制已安裝模塊的協議,並提供方法輸出協議信息

  • 提供一套穩定的公有 JS API,用於記錄構建工具的輸出信息

  • 可讀、最小化、美觀的 CLI 輸出信息

Yarn 用於生產環境

我們已經在 Facebook 中把 Yarn 用於生產環境,並且效果非常理想。Yarn 有效地管理了許多 JavaScript 項目的包依賴關係。在每次遷移時,構建都可以離線進行,因此加速了工作流程。我們基於 React Native 在不同條件下進行安裝時間測試,比較了 Yarn 與 npm 的性能,具體參見這裡。

起步

最簡單的起步方法是:

npm install -g yarnpkgyarn

yarn CLI 代替了原有開發工作流中 npm CLI 的作用,用法可能是單純的替代,也可能是一個新的、相似的命令:

  • npm install → yarn

不需要帶參數,`yarn` 命令會讀取 `package.json` 文件,然後從 npm 倉庫中抓取軟體包,並放置到 `node_modules` 目錄中。等價於運行 `npm install`。

  • npm install --save <name> → yarn add <name>

我們避免了 `npm install <name>` 命令中安裝「不可見的依賴」的行為,並分離出一個新命令。運行 `yarn add <name>` 等價於運行 `npm install --save <name>`。

未來

目前已經有許多成員一起參與到 Yarn 的構建中,以解決我們的共同問題,我們也希望 Yarn 未來能真正成為一個大眾化的社區項目。Yarn 目前已經 在 GitHub 開源 ,我們也已經準備好向 Node 社區進行推廣:使用 Yarn、分享構思、編寫文檔、互相支持,並幫助構建一個很棒的社區來進行長期維護。我們相信 Yarn 已經擁有一個良好的開局,如果有你的幫助,Yarn 的未來將會更加美好。


推薦閱讀:

npx: npm 5.2.0 內置的包執行器
npm的應用場景,剛需在何處?簡單的寫寫頁面是否需要npm包管理工具?
npm install 生成的package-lock.json是什麼文件?有什麼用?
為什麼 npm 要為每個項目單獨安裝一遍 node_modules?

TAG:Facebook | YARN | npm |