主流包管理工具npm、yarn、cnpm、pnpm之間的區別與聯繫——原理篇

接觸 node 以後,一直使用npm包管理工具, cnpm 一開始會用一些,可是並無以爲比 npm 快得多,使用 cnpm 的時候還常常安裝不成功,只能再用 npm 安裝一遍,漸漸的就棄用了 cnpm 。html

最近在看《MongoDB高級技術棧全覆蓋前端 Vue+Node+MongoDB高級全棧開發》課程,才知道還有其餘包管理工具,藉此機會,好好的整理一下各個包管理工具之間的區別和聯繫。前端

NPM

npm 是 Node.js 可以如此成功的主要緣由之一。npm 團隊作了不少的工做,以確保 npm 保持向後兼容,並在不一樣的環境中保持一致。node

npm是圍繞着 語義版本控制(semver)的思想而設計。git

給定一個版本號:主版本號.次版本號.補丁版本號, 如下這三種狀況須要增長相應的版本號:github

  • 主版本號: 當API發生改變,並與以前的版本不兼容的時候
  • 次版本號: 當增長了功能,可是向後兼容的時候
  • 補丁版本號:當作了向後兼容的缺陷修復的時候

npm使用一個名爲package.json的文件,用戶能夠經過npm install --save命令把項目裏全部的依賴項保存在這個文件裏。

例如,運行npm install --save lodash會將如下幾行添加到package.json文件中。npm

  1.  
    "dependencies": {
  2.  
    "lodash": "^4.17.4"
  3.  
    }

解析,json

  • ^字符,告訴npm,安裝主版本等於4的任意一個版本便可
  • 如今運行npm進行安裝,npm將安裝lodash的主版本爲4的最新版,多是 lodash@4.25.5(@是npm約定用來肯定包名的指定版本的)
  • 理論上,次版本號的變化並不會影響向後兼容性。所以,安裝最新版的依賴庫應該是能正常工做的,並且能引入自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,那麼依賴樹將以下所示:

  1.  
    node_modules
  2.  
    - package-A
  3.  
    -- node_modules
  4.  
    --- package-B
  5.  
    ----- node_modules
  6.  
    ------ package-C
  7.  
    -------- some-really-really-really-long-file-name-in-package-c.js

這個結構可能會很長。這對於基於Unix的操做系統來講只不過是一個小煩惱,但對於Windows來講倒是個破壞性的東西,由於有不少程序沒法處理超過260個字符的文件路徑名。

npm 3採用了扁平依賴關係樹來解決這個問題,因此咱們的3個項目結構如今看起來以下所示:

  1.  
    node_modules
  2.  
    - package-A
  3.  
    - package-B
  4.  
    - package-C
  5.  
    -- 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必須首先遍歷全部的項目依賴關係,而後再決定如何生成扁平的node_modules目錄結構。npm必須爲全部使用到的模塊構建一個完整的依賴關係樹,這是一個耗時的操做,是npm安裝速度慢的一個很重要的緣由

想固然的覺得每次運行npm install命令時,NPM都得從互聯網上下載全部內容。

可是,npm是有本地緩存的,它保存了已經下載的每一個版本的壓縮包。本地緩存的內容能夠經過npm cache ls命令進行查看。本地緩存的設計有助於減小安裝時間。

總而言之,npm是一個成熟、穩定、而且有趣的包管理器。

cnpm

  • cnpm跟npm用法徹底一致,只是在執行命令時將npm改成cnpm。
  • npm安裝插件是從國外服務器下載,受網絡影響大,可能出現異常,若是npm的服務器在中國就行了,因而淘寶團隊幹了這事。來自官網:「這是一個完整 npmjs.org 鏡像,你能夠用此代替官方版本(只讀),同步頻率目前爲 10分鐘 一次以保證儘可能與官方服務同步。」
  • 官方地址:http://npm.taobao.org
  • 安裝:$ npm install -g cnpm --registry=https://registry.npm.taobao.org

Yarn

Yarn發佈於2016年10月,截至當前2018年7月,在Github上擁有了32.2k個Star。而npm只有16.8k個Start。這個項目由一些高級開發人員維護,包括了Sebastian McKenzie(Babel.js)和Yehuda Katz(Ember.jsRustBundler等)。

Yarn一開始的主要目標是解決上一節中描述的因爲語義版本控制而致使的npm安裝的不肯定性問題。雖然可使用npm shrinkwrap來實現可預測的依賴關係樹,但它並非默認選項,而是取決於全部的開發人員知道而且啓用這個選項。

Yarn採起了不一樣的作法。每一個yarn安裝都會生成一個相似於npm-shrinkwrap.jsonyarn.lock文件,並且它是默認建立的。除了常規信息以外,yarn.lock文件還包含要安裝的內容的校驗和,以確保使用的庫的版本相同。

yarn是通過從新設計的嶄新的npm客戶端,它能讓開發人員並行處理全部必須的操做,並添加了一些其餘改進。

  • 運行速度獲得了顯著的提高,整個安裝時間也變得更少
  • 像npm同樣,yarn使用本地緩存。與npm不一樣的是,yarn無需互聯網鏈接就能安裝本地緩存的依賴項,它提供了離線模式。這個功能在2012年的npm項目中就被提出來過,但一直沒有實現。
  • 容許合併項目中使用到的全部的包的許可證

一般狀況下不建議經過npm進行安裝。npm安裝是非肯定性的,程序包沒有簽名,而且npm除了作了基本的SHA1哈希以外不執行任何完整性檢查,這給安裝系統程序帶來了安全風險。

 

npm install -g yarn

強烈建議你經過最適合於你的操做系統的安裝方法來安裝yarn,進官網下載對應版本

yarn官方地址:https://yarnpkg.com/zh-Hans/ 點擊打開連接

pnpm

可閱讀pnpm的做者Zoltan Kochan發表的「爲何要用pnpm?

  • pnpm運行起來很是的快,超過了npm和yarn
  • pnpm採用了一種巧妙的方法,利用硬連接和符號連接來避免複製全部本地緩存源文件,這是yarn的最大的性能弱點之一
  • 使用連接並不容易,會帶來一堆問題須要考慮。
  • pnpm繼承了yarn的全部優勢,包括離線模式和肯定性安裝

總結

  • npm仍然提供了一個很是有用的解決方案,支持大量的測試用例。大多數開發人員使用原始npm客戶端仍然能夠作得很好
  • yarn的肯定性安裝,能夠避免不少潛在的問題,相對安全
  • pnpm多是一些測試用例的更好的選擇。例如,它能夠在運行大量集成測試並但願儘量快地安裝依賴關係的中小型團隊中發揮做用

與君共勉:再牛逼的夢想,也抵不住傻逼般的堅持! 

相關文章
相關標籤/搜索