本文作者對照了當前主流的包管理工具npm、yarn、pnpm之間的區分,並提出了適宜的運用發起,以下為譯文:
NPM
npm是Node.js能夠云云勝利的重要緣由之一。npm團隊做了許多的事變,以確保npm堅持向後兼容,並在差別的環境中堅持一致。
npm是圍繞着語義版本掌握(semver)的頭腦而設想的,下面是從他們的網站摘抄過來的:
給定一個版本號:主版本號.次版本號.補丁版本號, 以下這三種狀況須要增添響應的版本號:
- 主版本號: 當API發作轉變,並與之前的版本不兼容的時刻
- 次版本號: 當增添了功用,然則向後兼容的時刻
- 補丁版本號: 當做了向後兼容的瑕玷修復的時刻
npm運用一個名為package.json的文件,用戶能夠經由過程npm install –save敕令把項目里一切的依靠項保留在這個文件里。
比方,運轉npm install –save lodash會將以下幾行添加到package.json文件中。
"dependencies": {
"lodash": "^4.17.4"
}
請注重,在版本號lodash之前有個^字符。這個字符通知npm,裝置主版本即是4的恣意一個版本即可。所以假如我如今運轉npm舉行裝置,npm將裝置lodash的主版本為4的最新版,多是 lodash@4.25.5(@是npm商定用來肯定包名的指定版本的)。你能夠在此處檢察一切支撐的字符:https://docs.npmjs.com/misc/semver。
理論上,次版本號的變化並不會影響向後兼容性。因而,裝置最新版的依靠庫應該是能一般事變的,而且能引入自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 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 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裝置速率慢的一個很重要的緣由。
由於我沒有細緻相識npm的變化,所以我想當然的以為每次運轉npm install敕令時,NPM都得從互聯網上下載一切內容。
然則,我錯了,npm是有當地緩存的,它保留了已下載的每一個版本的壓縮包。當地緩存的內容能夠經由過程npm cache ls敕令舉行檢察。當地緩存的設想有助於削減裝置時間。
總而言之,npm是一個成熟、穩固、而且風趣的包管理器。
Yarn
Yarn宣告於2016年10月,並在Github上敏捷具有了2.4萬個Star。而npm只要1.2萬個Star。這個項目由一些高等開發人員保護,包括了Sebastian McKenzie(Babel.js)和Yehuda Katz(Ember.js、Rust、Bundler等)。
從我彙集到的狀況來看,Yarn一最先的重要目的是處置懲罰上一節中形貌的由於語義版本掌握而致使的npm裝置的不肯定性題目。雖然能夠運用npm shrinkwrap來完成可展望的依靠關聯樹,但它並非默許選項,而是取決於一切的開發人員曉得而且啟用這個選項。
Yarn採用了差別的做法。每一個yarn裝置都邑天生一個類似於npm-shrinkwrap.json的yarn.lock文件,而且它是默許建立的。除了通例信息以外,yarn.lock文件還包括要裝置的內容的校驗和,以確保運用的庫的版本雷同。
由於yarn是極新的經由從新設想的npm客戶端,它能讓開發人員并行化處置懲罰一切必需的操縱,並添加了一些其他革新,這使得運轉速率得到了明顯的提拔,全部裝置時間也變得更少。我預計,速率提拔是yarn受歡迎的重要緣由。
像npm一樣,yarn運用當地緩存。與npm差別的是,yarn無需互聯網銜接就可以裝置當地緩存的依靠項,它供應了離線形式。這個功用在2012年的npm項目中就被提出來過,但一向沒有完成。
yarn還供應了一些其他革新,比方,它許可兼并項目中運用到的一切的包的許可證,這一點讓人很愉快。
一個風趣的事變是,yarn文檔的立場最先針對npm發作轉變,由於yarn項目變得流行起來。
最最先的yarn通告是這麼引見yarn的裝置的:
*最簡樸的入門要領是運轉:
npm install -g yarn
yarn*
如今的yarn裝置頁面是這麼說的:
注重:通常狀況下不發起經由過程npm舉行裝置。npm裝置黑白肯定性的,順序包沒有署名,而且npm除了做了基礎的SHA1哈希以外不實行任何完全性搜檢,這給裝置體系順序帶來了平安風險。
基於這些緣由,強烈發起你經由過程最適合於你的操縱體系的裝置要領來裝置yarn。
以這類速率發展下去的話,假如yarn要宣告他們本身的registry,讓開發者逐步鐫汰npm的話,我們一點都不會覺得驚奇。
看起來好像要謝謝yarn,npm終究意想到他們須要越發關注一些人人強烈要求的題目了。當我在考核我之前提到的強烈要求的“離線”功用時,我注重到這個需求正在被积極地修復當中。
pnpm
正如我所提到的,在pnpm的作者Zoltan Kochan宣布了“為何要用pnpm?”以後,我才曉得pnpm。
我不會引見太多的細節(由於這篇文章已宣告很久了),然則你能夠檢察我的最初的帖子來尋覓更多的內容,同時在Twitter上到場議論。
然則
我想指出的是,pnpm運轉起來異常的快,以至凌駕了npm和yarn。
為何這麼快呢? 由於它採用了一種奇妙的要領,應用硬鏈接和標記鏈接來防止複製一切當地緩存源文件,這是yarn的最大的機能缺點之一。
運用鏈接並不輕易,會帶來一堆題目須要斟酌。
正如Sebastian在Twitter上指出的那樣,他最初是打算在yarn中運用標記鏈接的,然則由於其他一些緣由摒棄了它。
同時,正如在Github上具有2000多個Star那樣,pnpm能夠為許多人所用。
別的,停止2017年3月,它繼續了yarn的一切長處,包括離線形式和肯定性裝置。
總結
我以為yarn和pnpm的開發人員做了一個驚人的事變。我個人喜好的是肯定性裝置,由於我喜好掌握,我不喜好欣喜。
不管這場合作的效果是什麼,我很謝謝yarn在npm的腳下點了一把火,供應了別的一個挑選。
我確信yarn是一個更平安的挑選,然則pnpm多是一些測試用例的更好的挑選。比方,它能夠在運轉大批集成測試並願望盡量快地裝置依靠關聯的中小型團隊中發揮作用。
末了,我以為,npm依然供應了一個異常有效的處置懲罰方案,支撐大批的測試用例。大多數開發人員運用原始npm客戶端依然能夠做得很好。