我們為什么要從NPM切換到Yarn
今年10月,非死book發布了 Yarn 一個新的JavaScript包管理CLI客戶端工具,意圖與NPM相競爭。起初,包括我自己在內很多人對此均持懷疑態度,這確實也很合理。同一生態下的多依賴管理解決方案,事情很容易就會變得復雜。那在這種讓人復雜地讓人感到痛苦的情況下,任何增加復雜性(的行為)都是極其糟糕的。
Yarn發布初始版本之后,我體驗了Yarn大約15分鐘,得出的結論是:Yarn是為了解決非常大型機構面臨的問題。而NPM則能滿足我們所有的需求,所以為什么我要投入時間和精力去學習一個根本不會給我帶來什么價值的東西呢?
問題
Yarn起始于我們前端一次失敗的構建。在長達幾個月里,我們間歇性地遭遇構建失敗,并且發現是由于使用NPM安裝命令不正確安裝的模塊,或者一些模塊作者破壞性的修改(而并非遵循類似于版本控制要求的修改)所引起的。因為NPM允許指定范圍內的包版本安裝,那么就會造成補丁或者版本修復就會自動應用到項目中的副作用,這可能就會造成同一個“package.json”(NPM配置文件)在不同的機器內根據相同的dependencies卻安裝了不同版本的庫。而這根本就不是我們想要的。
對于我們來說,這些問題是間歇發生的,并且臨時采取的解決方案特別多:重新構建、希望NPM安裝正確的模塊、或者將出問題的包降級等。任何一名JS開發者都知道,追蹤由于NPM安裝引起的錯誤極其困難,因為NPM的錯誤提示信息根本沒什么用,再加上大多數的JS模塊一般都依賴若干其他的JS模塊,這反過來就會追蹤到更多的模塊上,如此遞歸。
深入研究
某一天,由于transpilation(ES6轉換為ES5的過程)出錯引發構建失敗。我檢查代碼版本并問了團隊其他成員是否有人不小心提交了一個破壞性修改到Webpack配置文件上,或者顯式(主動)升級了依賴版本。很不幸,(都沒能找到原因)。
我嘗試重新構建了一下,還是同樣的報錯信息。每次構建時,典型的構建過程就是會下載全新的依賴包。無奈之下,我參考著三天前備份在電腦里的node_modules目錄下的依賴在本地構建成功了。然后,我徹底刪除了node_modules目錄再重新運行npm install,他會根據全新安裝的依賴組件來重新構建。最開始還是會報之前同樣的錯誤。我繼續刪除了node_modules目錄再重新安裝,相同的報錯又出現了。我讓我同時Michael將他本地的node_modules目錄打包發給我(他的包是五天前的)。我參考這些組件重新構建最后成功了。所以我很確定錯誤是由于依賴模塊引起的,并且打算弄明白到底是哪一個依賴模塊。
30分鐘過后,經過Google和查閱Github issues,我將引起問題的模塊定位到babel轉換重新生成器插件上。(針對這個模塊)幾個月以來都沒有提交過修改,并且網上針對這個問題為什么出現也沒有令人滿意的解釋。但是這個模塊的幾個不同版本6.5.2, 6.9.0, 6.20.0作為其他包的依賴被安裝過。6.9.0版本的依賴自動升級為6.20.0版本。這對于6.9.0版本允許升級來說是正確的,然而6.5.2版本也允許升級但卻并沒有自動升級。所有這些版本都應該升級到6.20.0版本。
當你在node中通過require()方法引入一個包的時候,相應的依賴也會隨后被緩存起來(引入)。當下次一個文件引入相同的包時,緩存起來的依賴就會用上。但這里存在一個問題:當6.5.2版本的包第一次引入的時候,相應地依賴被緩存到了本地。之后,當本地緩存的依賴被不同的模塊需要的時候,就會優先去緩存版本中去獲取相應地依賴,而不是根據要求的那樣去引入。那所有的模塊都應當使用的是6.20.0版本,但是因為6.5.2版本的緩存可用,那么就會優先使用6.5.2版本的緩存版本依賴,而6.5.2版本的依賴是用于構建已經過期的項目的。
我通過強制NPM安裝最新的依賴版本6.20.0來解決這個錯誤,但是我明白當我重新安裝依賴時,這個問題很有可能會重現,因此我并不滿意這個解決方案。
毫無疑問,我刪除了node_modules目錄,并重新安裝,依賴就回到了不匹配的狀態。
解決方案
我的同事Boris,推薦我使用Yarn代替NPM來安裝依賴。開始我并不相信這樣就會解決所有問題,但是我也沒有了其他方案了,就姑且試一試吧,畢竟也沒有原因證明Yarn并沒有什么特別的。
當使用Yarn安裝依賴后,構建成功了!我很驚訝!我檢查了node_modules目錄中安裝的是哪一個版本的依賴,結果是僅安裝了6.20.0版本。對比NPM,Yarn是根據Commit版本號層級來安裝依賴,而NPM僅僅依據的是package.json中的說明。對于怎么安裝不同版本的依賴,或者安裝最少的不同版本上,Yarn顯得更加智能,升級后的依賴版本通常是可以被升級的。
如果一個包有多個版本,而且要求版本已經安裝了,NPM就會使用已經安裝的版本,而并非指定的版本,這樣就會造成NPM偶然跳過安裝升級版本。這種版本號的不匹配,無論多小,都是NPM一部分不可接受的錯誤。事實證明,這種矛盾越來越讓人擔憂。如果沒有好好梳理以花長時間挖掘依賴樹,很難發現是哪里出現了問題。
在這個過程中兩樣東西很明確了:Yarn比NPM要快很多很多。Yarn也不會報出很多警告以及你并不關心的其他輸出占滿你的終端。
好奇心驅使下,我好好研究了下Yarn是怎樣用更少的時間完成同樣的任務。
對比來看,NPM發送請求來下載包的時候,會一次全部執行完,并且每個包都是邊下載邊安裝。這意味著,如果你項目中有15個依賴包,就會一個接著一個地下載和安裝,無論任一一個包的日志都可能輸出到控制臺。
然而,Yarn處理這個過程更為精細化了,當Yarn發起下載包請求的時候,會并行執行。如果你項目中有15個依賴,這些依賴會在同一時間全部下載完。當所有依賴全部下載之后,Yarn會安裝要求安裝的包(并不是所有的模塊都需要安裝),并且顯示在安裝該包過程中的任意可能結果。
以下展示的情景就是為什么Yarn更令人滿意的原因了:
你正在使用NPM安裝包。包會在同一時間下載和安裝,中途某個時候,一個包拋出了一個錯誤,但是NPM會繼續下載和安裝包。因為NPM會把所有的日志輸出到終端,有關錯誤包的錯誤信息就會在一大堆NPM打印的警告中丟失掉,并且你甚至永遠不會注意到實際發生的錯誤。
為了避免這個問題,當下載和安裝過程完成后,Yarn把錯誤消息放在了前面。
結論
Yarn提供了處理過程中更多的可見性,而NPM則傾向于將其模糊掉。當使用Yarn做了實驗并且發現所提供的好處之后,我們決定做此轉變。Yarn是多年來使用NPM管理JS依賴實踐后的產品,并且致力于解決JS開發者遇到的諸多問題,而這些問題往往將其歸因于“JavaScript工作方式”(而作為一個常見問題存在著而很難理解)。
如果在使用NPM過程中有任何不爽,強烈鼓勵你選擇Yarn!謝謝閱讀。
來自:http://www.zcfy.cc/article/why-we-switched-from-npm-to-yarn-2140.html