[譯] npm, yarn以及pnpm的不一樣之處

npm, yarn以及pnpm的不一樣之處

原文:Overview of differences between npm, yarn and pnpmhtml

譯者:neal1991node

welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact megit

LICENSE: MITgithub

我並非一個包管理器的專家。相反,直到最近我才意識到npm使用的是本地的緩存。在不知道這個時候,我寫了這篇名爲個人天--NPM克隆終於有意義了而且給出了一些個人不正確的猜測。這些反饋迫使我回頭而且從新審視近來這些包管理器的區別。npm

我在過去的5年時間一直都是使用npm。我也折騰了下yarn當它第一次出來的時候,並且我是經過一個禮拜前的文章「爲何咱們應該使用pnpm」來學習pnpm的。json

過去的一週,我一直花時間閱讀npmyarn以及pnpm相關的東西,想總結一下而後分享個人發現。個人目標讀者是長期的npm用戶,而且不肯意花費太多的時間瞭解有多少種npm的替代品,好比我本身。我只會關注這三種最常說起的(對於我)而且不會包括:iednpm-install以及npmd,由於對於它們我是一無所知。緩存

還有重要的一點須要指出,直到寫這篇文章的時候,尚未一個具備競爭力的庫的目標是替換NPM的registry(就是存儲包的地方),它們的目的都是替換npm命令行客戶端,提供另一個可用的用戶界面以及行爲,而且其功能也是相似的。安全

NPM

npm自從Node.js出現的那一天就存在了而且也是形成Node.js這個項目如此成功的緣由之一。npm團隊在讓npm保持向後兼容以及在多種環境下持續工做都作了不少的工做。babel

npm的設計理念是根據 Semantic Versioning (semver),這是一個至關直白的方法能夠從他們官網的引用能夠看出來。網絡

給定一個版本號MAJOR.MINOR.PATCH,增量修改表示:

  • MAJOR版本修改意味着你作出了不兼容的API變化。

  • MINOR版本意味着你以向後兼容的方式增長了功能。

  • PATCH版本意味着你作出了向後兼容的bug修復。

npm使用一個叫作package.json的文件,用戶能夠存儲項目的全部依賴經過運行npm install --save

例如,運行npm install --save loadsh將會將這一條加入到package.json之中。

"dependencies": {
  "loadsh": "^4.17.4"
}

注意^這個loadsh版本號以前的符號。這個符號告訴npm安裝任何與MAJOR版本相同的包。所以若是一年後我運行npm installnpm會安裝MAJOR版本號爲4的最新版本的loadsh。例如,它能夠是loadsh@4.25.5@是一個npm慣例用來指定包名的版本)。你能夠在這看到全部支持的符號: https://docs.npmjs.com/misc/semver

這樣作的緣由是,由於MINOR版本的變更(理論上)應該只是包含向後兼容的濱化。所以安裝最新版本的包可能會引入重要的bug或者安全問題,由於最初安裝的版本是4.17.4

另外一方面,它可能會致使多個開發者的機器上安裝了不一樣版本的包,即便他們是共享同一個package.json文件,這樣會潛在地致使難以調試以及「在個人機器是好的啊」的情形。

大多數npm包都很是依賴其它的npm包。這會致使循環依賴以及增長了版本不匹配的可能。

能夠經過npm config set save-exact true命令來關閉在包的版本前添加^的默認行爲,但這個只會鎖住高層次的依賴。由於每個引入的包都有它們本身的package.json文件,在這裏面的依賴可能包含了^,沒有辦法經過package.json來保證嵌套的內容。

爲了解決這個問題,npm提供了一個shrinkwrap命令。這個命令可以生成一個npm-shrinkwrap.json文件,對於全部的包以及嵌套的依賴規定了明確的版本。

這也就是說,即便是經過npm-shrinkwrap.json這個文件,npm也只是鎖住了包的版本而不是包的內容。即便npm如今阻止用戶屢次發佈相同版本的包npm管理依然有權利強制更新某些包。

下面是 shrinkwrap文檔頁面的引用:

若是你但願鎖定包內包含的指定字節,例如你有百分之百的信心可以從新發布或者構建,那麼你應該將你的依賴檢查到源代碼控制中,或者追求其它的某種可以驗證內容而不是版本的機制。

npm version 2過去式對於每一個包內全部引入的依賴所有都安裝。若是已有一個項目,這個項目引入項目A,項目A引入項目B,項目B引入項目C,那麼這個全部依賴的結構樹看起來會是下面這樣:

node_modules
- package-A
-- node_modules
--- package-B
----- node_modules
------ package-C
-------- some-really-really-really-long-file-name-in-package-c.js

這個結構可能會變得至關長。這可能僅僅是基於Unix系統上面的一個煩惱,在Windows上已經有不少破解程序可以解決文件路徑超過260個字符的問題。

npm version 3經過展平依賴樹來解決這個問題,所以這3個項目的結構看起來會是這個樣子的:

node_modules
- package-A
- package-B
- package-C
-- some-file-name-in-package-c.js

這個變化的結果就是改變了一寫長文件的文件路徑從 ./node_modules/package-A/node_modules/package-B/node-modules/some-file-name-in-package-c.js 變化爲 ./node_modules/some-file-name-in-package-c.js

你能夠從瞭解更多關於NPM 3依賴的解決方案。

這個方法的一個缺點是npm如今必需要遍歷全部的項目依賴從而決定如何展平node_modules文件夾。npm被強制爲全部使用過的模塊創建依賴樹,這樣作的代價會很大。這也是致使npm install安裝速度變慢的緣由之一。(請看文末的更新)。

由於我沒有仔細關注過npm的變化,我猜測NPM速度變慢的緣由是我每次運行npm install的時候都須要從網上下載全部東西。

事實證實,我是錯的,而且npm確實是具備本地緩存的,在其中保存了全部下載包的壓縮文件。能夠經過npm cache ls命令來查看本地緩存的內容。經過本地緩存能夠加快安裝速度。

總而言之,npm是一個成熟的,穩定的而且樂於使用的包管理器。

yarn

Yarn 是在2016年10月份發佈的而且在Github上迅速獲取了24K+star。做爲對比,npm 僅僅只有12K+ star。這個項目具備高資質的開發者好比Sebastian McKenzie (Babel.js) 以及 Yehuda Katz (Ember.js, Rust, Bundler 等等)。

從我目前收集的來看,yarn的最初的主要目的是針對npm因爲以前章節說起的semver相關行爲致使的安裝的不肯定性。然而可預測的依賴樹(若是須要的話)可以經過npm shrinkwarp來完成,這不是默認的行爲而且依賴於開發者瞭解這一選項而且來進行相應的操做。

Yarn採起了一個不一樣的方法。每次yarn安裝都會生成一個和npm-shrinkwrap.json相似的yarn.lock文件,可是它是默認產生的。除了常規信息,yarn.lock文件還包含了安裝內容的檢查從而確保使用相同版本的包。

由於yarn是一個才重寫的npm客戶端,開發者可以適宜地並行全部須要的操做而且增長一些改進,這同時也顯著提高了總體地安裝時間。我認爲速度地加快是yarn流行的主要緣由。

npm同樣,yarn也使用了本地緩存。可是不像npmyarn在安裝已經緩存的依賴的時候並不須要網絡鏈接,提供了一種offline模式。這個特性在npm上自從2012年就收到了請求,可是一直沒有獲得解決。

Yarn提供一些其它的好處。好比,它容許聚合項目中使用的全部的licence,而且很容易看到。

有意思的一點事,yarn文檔對於npm態度的轉變自從其變得流行以後。

最初的yarn發佈的時候說的安裝yarn的步驟是:

最容易開始運行的方式是:

npm install -g yarn
yarn

如今yarn關於安裝yarn的方式是

注意:不建議使用npm來進行安裝。npm是非肯定性的,包是沒有簽名的,npm僅僅是作了基本的SHA1 哈希並無作任何總體性檢查,這對於安裝系統級別的應用是有風險的。

鑑於以上緣由,強烈建議你經過適合你操做系統的安裝方法來安裝yarn。

以這種速度,即便yarn宣佈它們本身的registry從而讓開發者緩慢淘汰npm我都不以爲驚訝。

一樣也是因爲yarnnpm終於一是到它們須要密切關注那些強烈請求的issue。NPM最初對於yarn發佈的迴應在我看來是「它是可愛的」。如今,當我從新看那個被強烈要求的我以前提到過的「離線」特性已經有在被積極地解決,在咱們討論這一點的時候

pnpm

正如我以前所說起的那樣,我只是才知道pnpm 不久經過閱讀Zoltan Kochan的「爲何咱們應該使用pnpm?」,他就是npm的做者。

我不想設計到過多的細節(由於這篇文章已經很長了),可是你能夠從我最初的博文中能夠了解一些以及推特上的討論

可是

我想指出的是pnpmnpm以及yarn都要快

爲何它這麼快的緣由?由於它採用了巧妙的方式,利用硬連接和符號連接,以免複製全部本地緩存的源文件,這是戰勝yarn在性能上最主要的一方面。

使用連接並不容易,須要考慮一系列的問題。

正如Sebastian在推特上所指出的,他最初是想在yarn裏面使用符號連接,但最後由於不少緣由來對抗yarn。

同時,這個項目在Github上也具備2K+star,pnpm可以讓連接爲不少人工做。

另外,自從2017年3月它提供了全部yarn提供的優勢,包括離線模式以及肯定性安裝。

結論

我認爲yarnpnpm的開發者都作了很好的工做。我我的的偏好是肯定性的安裝,由於我喜歡本身掌控而且我不喜歡驚喜。

不管最後競爭的結果是什麼(這也提醒了我io.js fork),我感謝yarnnpm帶來的這些麻煩所以塵埃落定以前還有不少選擇餘地。

我也認爲yarn可能很早以前就考慮過硬連接以及軟連接。我想知道的是yarn團隊針對這個想法會作些什麼,取決於pnpm形成的殺傷以及用戶對於安裝速度重視的程度。

我確實認爲總的來講yarn是一個安全的選擇,可是pnpm在某些案例上多是一個更好的選擇。好比,對於一個須要運行不少集成測試而且但願儘量提升安裝依賴速度的中小型團隊。

最後值得說得一點是,我認爲npm依然提供了對於大多數用戶案例很是有用的解決方案。大多數開發者能夠繼續只使用npm客戶端。

不管是任何狀況,我都感謝全部的努力保持生態健康的競爭者。當公司競爭的時候,獲利的是用戶。

來自於一下@ReBeccaOrg推特的更新。

  • FYI, 展平不是「遍歷整棵樹」的來源。遍歷整棵樹是關於自愈。

  • 和npm@2相比,它減緩了速度。

  • 若是你經過rm -fr來刪除嵌套的依賴而且經過npm install來安裝,你會注意到這可能幫你解決問題。

  • 如今事實證實,npm@1-@npm@4緩存是緩慢的,很是慢。慢的超乎想象。常常每每是比在一個快速網絡中下載快一點點。所以隨着npm@5的重寫(自從npm1.5就開始計劃)它忽然變得快多了。

  • yarn經過沙箱能夠傷你免於http://registry.npmjs.org 敵對的限制,可是並不抱進一步的保證。

相關文章
相關標籤/搜索