原文鏈接 : Yarn: A new package manager for JavaScript
原文作者 : SEBASTIAN MCKENZIE,CHRISTOPH POJER氨淌,JAMES KYLE
譯文出自 : 掘金翻譯計(jì)劃
譯者 : 達(dá)仔
校對(duì)者: 根號(hào)三
JavaScript 包管理方式在 Facebook 的演變
在包管理工具出現(xiàn)之前志笼,JavaScript 工程師們通常依賴(lài)的項(xiàng)目并不多撬讽,因此會(huì)把依賴(lài)直接存儲(chǔ)在工程目錄或上傳到 CDN 上懊缺。在 Node.js 出現(xiàn)后不久袱巨,第一個(gè)主流的 JavaScript 包管理工具 npm
被引入進(jìn)來(lái),并很快成為了最受歡迎的包管理工具之一往核。從此,新的開(kāi)源項(xiàng)目不斷涌現(xiàn)嚷节,工程師們比起以前更加樂(lè)于分享代碼了聂儒。
在 Facebook 中,我們有很多項(xiàng)目都要依賴(lài) npm
倉(cāng)庫(kù)上的代碼丹喻,比如 React薄货。但隨著內(nèi)部規(guī)模的擴(kuò)大,我們面臨著以下挑戰(zhàn):在跨平臺(tái)與跨用戶之間安裝依賴(lài)時(shí)的代碼一致性問(wèn)題碍论、在安裝依賴(lài)時(shí)花費(fèi)太長(zhǎng)時(shí)間谅猾、以及 npm
客戶端自動(dòng)執(zhí)行某些依賴(lài)庫(kù)的代碼所導(dǎo)致的安全性問(wèn)題。我們嘗試過(guò)尋找這些問(wèn)題的解決方案,但在這個(gè)過(guò)程中通常又會(huì)引起一些新的問(wèn)題税娜。
嘗試修改 npm 客戶端
在開(kāi)始階段坐搔,我們遵循了最佳實(shí)踐,在代碼倉(cāng)庫(kù)中只跟蹤了 package.json
文件的變化敬矩,并要求工程師手動(dòng)運(yùn)行 npm install
命令安裝依賴(lài)概行。這種模式在開(kāi)發(fā)人員的電腦上沒(méi)有問(wèn)題,但在持續(xù)集成環(huán)境中遇到了困難弧岳,因?yàn)槌鲇诎踩c可靠性的考慮凳忙,持續(xù)集成環(huán)境需要進(jìn)行沙箱隔離,不能進(jìn)行聯(lián)網(wǎng)禽炬,因此也無(wú)法安裝依賴(lài)涧卵。
接下來(lái),我們嘗試在代碼倉(cāng)庫(kù)中跟蹤整個(gè) node_modules
目錄的文件變化腹尖。雖然這種方式有效柳恐,卻使得一些簡(jiǎn)單操作變得復(fù)雜化了。比如热幔,對(duì) babel 更新一個(gè)次要版本號(hào)時(shí)乐设,會(huì)產(chǎn)生多達(dá) 800,000 行的提交記錄,此外由于 lint 規(guī)則的存在绎巨,引起無(wú)效的 utf-8 字節(jié)序列近尚、windows 換行符、非 png 壓縮圖片等問(wèn)題時(shí)认烁,將會(huì)導(dǎo)致工程師經(jīng)常需要花費(fèi)一整天的時(shí)間合并 node_modules
目錄的文件肿男。而我們負(fù)責(zé)源碼控制的團(tuán)隊(duì)也指出,跟蹤 node_modules
目錄會(huì)引入過(guò)多的元數(shù)據(jù)却嗡。比如 React Native 的package.json
文件目前只列出了68項(xiàng)依賴(lài)舶沛,但在運(yùn)行 npm install
后,node_modules
目錄整整包含了 121,358 個(gè)文件窗价。
最后如庭,為了有效組織 Facebook 逐漸增長(zhǎng)的工程師人數(shù)以及管理需要安裝的代碼量,我們嘗試修改npm
客戶端撼港。我們決定壓縮整個(gè) node_modules
目錄坪它,并上傳到內(nèi)部 CDN,然后我們的工程師與持續(xù)集成系統(tǒng)都能從 CDN 上下載并解壓文件帝牡,從而保證了代碼一致性往毡。這樣我們就可以從源碼控制系統(tǒng)中刪除數(shù)以萬(wàn)計(jì)的文件了,但不足之處是工程師現(xiàn)在不僅在拉代碼時(shí)需要聯(lián)網(wǎng)了靶溜,構(gòu)建也同樣需要聯(lián)網(wǎng)开瞭。
我們還試圖為 npm
的 shrinkwrap 功能尋求優(yōu)化方案懒震,這個(gè)工具是用來(lái)鎖定依賴(lài)版本號(hào)的。但Shrinkwrap
功能的文件默認(rèn)不會(huì)生成嗤详,如果開(kāi)發(fā)者忘記了生成這一步驟个扰,文件就不會(huì)被同步更新,因此我們編寫(xiě)了一個(gè)工具葱色,以確定 Shrinkwrap
的文件內(nèi)容和 node_modules
目錄中的文件相符递宅。這些文件由大量的 JSON 塊組成,并且鍵名是無(wú)序的苍狰,因此每次更改通常會(huì)導(dǎo)致 Shrinkwrap
文件的內(nèi)容大幅變化办龄,難以進(jìn)行代碼審查。為減緩這一問(wèn)題舞痰,我們還需要借助一個(gè)額外的腳本土榴,對(duì)所有條目進(jìn)行排序诀姚。
最后响牛,通過(guò) npm
升級(jí)單個(gè)依賴(lài)包時(shí),基于 語(yǔ)義化版本號(hào) 規(guī)則赫段,npm
通常會(huì)連同其他無(wú)關(guān)依賴(lài)一起更新呀打。這使得每次更新都會(huì)比預(yù)期產(chǎn)生更多的變化,工程師們認(rèn)為這樣把 node_modules
提交上傳到 CDN 的過(guò)程糯笙,難以達(dá)到預(yù)期的效果贬丛。
構(gòu)建新客戶端
與其圍繞 npm
客戶端繼續(xù)構(gòu)建基礎(chǔ)設(shè)施,不如從整體上再次回顧這些問(wèn)題给涕。倫敦辦公室的 Sebastian McKenzie 提出豺憔,如果我們建立一個(gè)新客戶端工具以代替 npm
客戶端,從而解決我們的核心問(wèn)題呢够庙?這一構(gòu)思很快得到了我們的認(rèn)同恭应,團(tuán)隊(duì)對(duì)于這個(gè)主意也感到非常興奮。
在開(kāi)發(fā)過(guò)程中耘眨,我們與業(yè)界的工程師們進(jìn)行了交流討論昼榛,發(fā)現(xiàn)他們也面臨著類(lèi)似的問(wèn)題,也嘗試過(guò)許多類(lèi)似的解決方案剔难,通常只能把這些問(wèn)題逐一解決胆屿。很明顯,有必要把整個(gè) JavaScript 社區(qū)正在面臨的問(wèn)題集合起來(lái)偶宫,然后我們就可以開(kāi)發(fā)一個(gè)主流的解決方案了非迹。在此感謝 Exponent、 Google 與 Tilde 的工程師們的協(xié)助纯趋,我們共同建立了 Yarn
客戶端憎兽,并在每一個(gè)主流 JS 框架以及 Facebook 外的使用場(chǎng)景中測(cè)試驗(yàn)證了 Yarn 的性能。今天(2016-10-11),我們很榮幸把這個(gè)工具開(kāi)源分享到社區(qū)中唇兑。
介紹 Yarn
Yarn
是一個(gè)新的包管理器酒朵,用于替代現(xiàn)有的 npm
客戶端或者其他兼容 npm
倉(cāng)庫(kù)的包管理工具。Yarn
保留了現(xiàn)有工作流的特性扎附,優(yōu)點(diǎn)是更快蔫耽、更安全、更可靠留夜。
任何包管理器的主要功能都是安裝某些軟件包匙铡,軟件包即用于特定功能的某段代碼,通常是從一個(gè)全局的倉(cāng)庫(kù)安裝到工程師的本地環(huán)境碍粥。每個(gè)軟件包可以依賴(lài)于其他包鳖眼,也可以不依賴(lài)。一個(gè)典型的項(xiàng)目結(jié)構(gòu)的依賴(lài)樹(shù)通常會(huì)包含數(shù)十個(gè)嚼摩、數(shù)百個(gè)甚至上千個(gè)軟件包钦讳。
這些依賴(lài)包通常是帶版本號(hào)的,通過(guò)語(yǔ)義化版本控制(semver)安裝枕面。Semver 定義的版本號(hào)反映了每個(gè)新版本更改的類(lèi)型愿卒,到底是進(jìn)行了不兼容的API改動(dòng)(MAJOR),還是添加了向后兼容的新特性(MINOR)潮秘,還是進(jìn)行了向后兼容的 bug 修復(fù)(PATCH)琼开。然而,semver 依賴(lài)于軟件包的開(kāi)發(fā)者不能犯錯(cuò)誤——如果依賴(lài)關(guān)系沒(méi)有加鎖枕荞,可能會(huì)引入一些破壞性更改或者產(chǎn)生新的 bug柜候。
結(jié)構(gòu)
在 Node 生態(tài)系統(tǒng)中,依賴(lài)通常安裝在項(xiàng)目的 node_modules
文件夾中躏精。然而渣刷,這個(gè)文件的結(jié)構(gòu)和實(shí)際依賴(lài)樹(shù)可能有所區(qū)別,因?yàn)橹貜?fù)的依賴(lài)可以合并到一起玉控。npm
客戶端把依賴(lài)安裝到 node_modules
目錄的過(guò)程具有不確定性飞主。這意味著當(dāng)依賴(lài)的安裝順序不同時(shí),node_modules
目錄的結(jié)構(gòu)可能會(huì)發(fā)生變化高诺。這種差異可能會(huì)導(dǎo)致類(lèi)似“我的機(jī)子上可以運(yùn)行碌识,別的機(jī)子不行”的情況,并且通常要花費(fèi)大量時(shí)間定位與解決虱而。
Yarn
通過(guò) lockfiles 文件以及一個(gè)確定性的筏餐、可靠的安裝算法,解決了版本問(wèn)題和 npm
的不確定性問(wèn)題牡拇。Lockfile 文件把安裝的軟件包版本鎖定在某個(gè)特定版本魁瞪,并保證 node_modules
目錄在所有機(jī)器上的安裝結(jié)果都是相同的穆律。Lockfile 還使用簡(jiǎn)潔的有序鍵名的格式,保證了每次的文件變化最小化导俘,進(jìn)行代碼審查也更為簡(jiǎn)單峦耘。
安裝過(guò)程分為以下三個(gè)步驟:
處理: Yarn
通過(guò)向代碼倉(cāng)庫(kù)發(fā)送請(qǐng)求,并遞歸查找每個(gè)依賴(lài)項(xiàng)旅薄,從而解決依賴(lài)關(guān)系辅髓。
抓取: 接下來(lái)少梁,Yarn
會(huì)查找全局的緩存目錄洛口,檢查所需的軟件包是否已被下載。如果沒(méi)有凯沪,Yarn 會(huì)抓取對(duì)應(yīng)的壓縮包第焰,并放置在全局的緩存目錄中,因此 Yarn
支持離線安裝妨马,同一個(gè)安裝包不需要下載多次挺举。依賴(lài)也可以通過(guò) tarball 的壓縮形式放置在源碼控制系統(tǒng)中,以支持完整的離線安裝身笤。
生成: 最后豹悬,Yarn
從全局緩存中把需要用到的所有文件復(fù)制到本地的 node_modules
目錄中。
通過(guò)清晰地細(xì)分這些步驟液荸,以及確定性的算法支持,使得 Yarn
支持并行操作脱篙,從而最大化地利用資源娇钱,并加速安裝進(jìn)程。在一些 Facebook 的項(xiàng)目上绊困,Yarn
甚至可以把安裝過(guò)程降低一個(gè)數(shù)量級(jí)文搂,從幾分鐘到只需幾秒鐘。Yarn
還使用了互斥鎖秤朗,以確保多個(gè) CLI 實(shí)例同時(shí)運(yùn)行時(shí)不會(huì)互相沖突與影響煤蹭。
縱觀整個(gè)過(guò)程,Yarn
對(duì)于軟件包安裝加上了嚴(yán)格的限制取视。你可以對(duì)哪個(gè)生命周期腳本作用于哪個(gè)軟件包進(jìn)行控制硝皂。軟件包的 checksum 也會(huì)存儲(chǔ)在 lockfile 中,以確保每一次安裝都可以得到同一個(gè)包作谭。
特性
Yarn
除了讓安裝過(guò)程變得更快與更可靠稽物,還添加了一些額外的特性,從而進(jìn)一步簡(jiǎn)化依賴(lài)管理的工作流折欠。
同時(shí)兼容 npm
與 bower 工作流贝或,并支持兩種軟件倉(cāng)庫(kù)混合使用
可以限制已安裝模塊的協(xié)議吼过,并提供方法輸出協(xié)議信息
提供一套穩(wěn)定的公有 JS API,用于記錄構(gòu)建工具的輸出信息
可讀咪奖、最小化盗忱、美觀的 CLI 輸出信息
Yarn 用于生產(chǎn)環(huán)境
我們已經(jīng)在 Facebook 中把 Yarn
用于生產(chǎn)環(huán)境,并且效果非常理想羊赵。Yarn
有效地管理了許多 JavaScript 項(xiàng)目的包依賴(lài)關(guān)系售淡。在每次遷移時(shí),構(gòu)建都可以離線進(jìn)行慷垮,因此加速了工作流程揖闸。我們基于 React Native 在不同條件下進(jìn)行安裝時(shí)間測(cè)試,比較了 Yarn
與 npm
的性能料身,具體參見(jiàn)這里汤纸。
起步
最簡(jiǎn)單的起步方法是:
npm install -g yarnpkgyarn
yarn
CLI 代替了原有開(kāi)發(fā)工作流中 npm
CLI 的作用,用法可能是單純的替代芹血,也可能是一個(gè)新的贮泞、相似的命令:
npm install
→ yarn
不需要帶參數(shù),yarn
命令會(huì)讀取 package.json
文件幔烛,然后從 npm 倉(cāng)庫(kù)中抓取軟件包啃擦,并放置到 node_modules
目錄中。等價(jià)于運(yùn)行 npm install
饿悬。
npm install --save <name>
→ yarn add <name>
我們避免了 npm install <name>
命令中安裝“不可見(jiàn)的依賴(lài)”的行為令蛉,并分離出一個(gè)新命令。運(yùn)行 yarn add <name>
等價(jià)于運(yùn)行 npm install --save <name>
狡恬。
未來(lái)
目前已經(jīng)有許多成員一起參與到 Yarn
的構(gòu)建中珠叔,以解決我們的共同問(wèn)題,我們也希望 Yarn
未來(lái)能真正成為一個(gè)大眾化的社區(qū)項(xiàng)目弟劲。Yarn
目前已經(jīng) 在 GitHub 開(kāi)源 祷安,我們也已經(jīng)準(zhǔn)備好向 Node 社區(qū)進(jìn)行推廣:使用 Yarn
、分享構(gòu)思兔乞、編寫(xiě)文檔汇鞭、互相支持,并幫助構(gòu)建一個(gè)很棒的社區(qū)來(lái)進(jìn)行長(zhǎng)期維護(hù)庸追。我們相信 Yarn
已經(jīng)擁有一個(gè)良好的開(kāi)局霍骄,如果有你的幫助,Yarn
的未來(lái)將會(huì)更加美好锚国。