原文: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
過去的一週,我一直花時間閱讀npm
,yarn
以及pnpm
相關的東西,想總結一下而後分享個人發現。個人目標讀者是長期的npm
用戶,而且不肯意花費太多的時間瞭解有多少種npm
的替代品,好比我本身。我只會關注這三種最常說起的(對於我)而且不會包括:ied
,npm-install
以及npmd
,由於對於它們我是一無所知。緩存
還有重要的一點須要指出,直到寫這篇文章的時候,尚未一個具備競爭力的庫的目標是替換NPM的registry(就是存儲包的地方),它們的目的都是替換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 install
,npm
會安裝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 是在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
也使用了本地緩存。可是不像npm
,yarn
在安裝已經緩存的依賴的時候並不須要網絡鏈接,提供了一種offline
模式。這個特性在npm上自從2012年就收到了請求,可是一直沒有獲得解決。
Yarn提供一些其它的好處。好比,它容許聚合項目中使用的全部的licence,而且很容易看到。
有意思的一點事,yarn
文檔對於npm
態度的轉變自從其變得流行以後。
最初的yarn發佈的時候說的安裝yarn
的步驟是:
最容易開始運行的方式是:
npm install -g yarn yarn
如今yarn關於安裝yarn的方式是:
注意:不建議使用npm來進行安裝。npm是非肯定性的,包是沒有簽名的,npm僅僅是作了基本的SHA1 哈希並無作任何總體性檢查,這對於安裝系統級別的應用是有風險的。
鑑於以上緣由,強烈建議你經過適合你操做系統的安裝方法來安裝yarn。
以這種速度,即便yarn
宣佈它們本身的registry從而讓開發者緩慢淘汰npm
我都不以爲驚訝。
一樣也是因爲yarn
,npm
終於一是到它們須要密切關注那些強烈請求的issue。NPM最初對於yarn發佈的迴應在我看來是「它是可愛的」。如今,當我從新看那個被強烈要求的我以前提到過的「離線」特性已經有在被積極地解決,在咱們討論這一點的時候。
正如我以前所說起的那樣,我只是才知道pnpm 不久經過閱讀Zoltan Kochan的「爲何咱們應該使用pnpm?」,他就是npm的做者。
我不想設計到過多的細節(由於這篇文章已經很長了),可是你能夠從我最初的博文中能夠了解一些以及推特上的討論。
可是
我想指出的是pnpm
比npm以及yarn都要快。
爲何它這麼快的緣由?由於它採用了巧妙的方式,利用硬連接和符號連接,以免複製全部本地緩存的源文件,這是戰勝yarn在性能上最主要的一方面。
使用連接並不容易,須要考慮一系列的問題。
正如Sebastian在推特上所指出的,他最初是想在yarn
裏面使用符號連接,但最後由於不少緣由來對抗yarn。
同時,這個項目在Github上也具備2K+star,pnpm
可以讓連接爲不少人工做。
另外,自從2017年3月它提供了全部yarn
提供的優勢,包括離線模式以及肯定性安裝。
我認爲yarn
和pnpm
的開發者都作了很好的工做。我我的的偏好是肯定性的安裝,由於我喜歡本身掌控而且我不喜歡驚喜。
不管最後競爭的結果是什麼(這也提醒了我io.js fork),我感謝yarn
給npm
帶來的這些麻煩所以塵埃落定以前還有不少選擇餘地。
我也認爲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 敵對的限制,可是並不抱進一步的保證。