1. 程式人生 > >Yarn 如果優化npm版本控制

Yarn 如果優化npm版本控制

今年10月,Facebook釋出了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!謝謝閱讀。