包是一段能夠複用的代碼,這段代碼能夠從全局註冊表下載到開發者的本地環境。每一個包可能會,也可能不會依賴於別的包。簡單地說,包管理器是一段代碼,它可讓你管理依賴(你或者他人寫的外部代碼),你的項目須要這些依賴來正確運行。
爲啥咱們須要一個包管理工具呢?由於咱們在Node.js
上開發時,會用到不少別人寫的JavaScript
代碼。若是咱們要使用別人寫的某個包,每次都根據名稱搜索一下官方網站,下載代碼,解壓,再使用,很是繁瑣。node
更重要的是,若是咱們要使用模塊A,而模塊A又依賴於模塊B,模塊B又依賴於模塊C和模塊D,npm
能夠根據依賴關係,把全部依賴的包都下載下來並管理起來。不然,靠咱們本身手動管理,確定既麻煩又容易出錯。react
因而一個集中管理的工具應運而生:數據庫
npm
官網上,若是要使用,直接經過npm
安裝就能夠直接用,不用管代碼存在哪,應該從哪下載。Yarn
是爲了彌補npm
的一些缺陷[速度慢,穩定性高]而出現的。」npm
爲你和你的團隊打開了鏈接整個JavaScript
天才世界的一扇大門。它是世界上最大的軟件註冊表,每星期大約有 30 億次的下載量,包含超過 600000 個 包(package
) (即,代碼模塊)。來自各大洲的開源軟件開發者使用npm
互相分享和借鑑。包的結構使您可以輕鬆跟蹤依賴項和版本。
下面是關於 npm
的快速介紹:npm
由三個獨立的部分組成:npm
package
)、設置參數以及管理 npm
使用體驗的主要途徑。registry
)package
)的信息。CLI
)CLI
經過命令行或終端運行。開發者經過 CLI 與 npm 打交道。Yarn
發佈於2016年10月,並在Github
上迅速擁有了2.4萬個Star。而npm
只有1.2萬個star
。這個項目由一些高級開發人員維護,包括了Sebastian McKenzie
(Babel.js)和Yehuda Katz
(Ember.js、Rust、Bundler等)。json
Yarn
一開始的主要目標是解決上一節中描述的因爲語義版本控制而致使的npm安裝的不肯定性問題。雖然可使用npm shrinkwrap
來實現可預測的依賴關係樹,但它並非默認選項,而是取決於全部的開發人員知道而且啓用這個選項。
Yarn採起了不一樣的作法。每一個yarn
安裝都會生成一個相似於npm-shrinkwrap.json
的yarn.lock
文件,並且它是默認建立的。除了常規信息以外,yarn.lock
文件還包含要安裝的內容的校驗和,以確保使用的庫的版本相同。
yarn的優化主要體如今:緩存
速度快 :安全
Yarn
有一個鎖定文件 (lock file) 記錄了被確切安裝上的模塊的版本號。每次只要新增了一個模塊,Yarn 就會建立(或更新)yarn.lock
這個文件。這麼作就保證了,每一次拉取同一個項目依賴時,使用的都是同樣的模塊版本。yarn
改變了一些npm
命令的名稱,好比 yarn add/remove
,感受上比 npm 本來的 install/uninstall
要更清晰。執行工程自身 preinstall
網絡
preinstall
鉤子此時會被執行。肯定首層依賴react-router
dependencies
和 devDependencies
屬性中直接指定的模塊(假設此時沒有添加 npm install
參數)。工程自己是整棵依賴樹的根節點,每一個首層依賴模塊都是根節點下面的一棵子樹,npm 會開啓多進程從每一個首層依賴模塊開始逐步尋找更深層級的節點。獲取模塊dom
package.json
中每每是 semantic version
(semver,語義化版本)。此時若是版本描述文件(npm-shrinkwrap.json
或 package-lock.json
)中有該模塊信息直接拿便可,若是沒有則從倉庫獲取。如 packaeg.json
中某個包的版本是 ^1.1.0,npm
就會去倉庫中獲取符合 1.x.x
形式的最新版本。resolved
字段),npm 會用此地址檢查本地緩存,緩存中有就直接拿,若是沒有則從倉庫下載。模塊扁平化(dedupe
)
loadsh
,B 模塊一樣依賴於 lodash
。在 npm3 之前會嚴格按照依賴樹的結構進行安裝,所以會形成模塊冗餘。yarn
和從 npm5
開始默認加入了一個 dedupe
的過程。它會遍歷全部節點,逐個將模塊放在根節點下面,也就是 node-modules
的第一層。當發現有重複模塊時,則將其丟棄。這裏須要對重複模塊進行一個定義,它指的是模塊名相同且 semver
兼容。每一個 semver
都對應一段版本容許範圍,若是兩個模塊的版本容許範圍存在交集,那麼就能夠獲得一個兼容版本,而沒必要版本號徹底一致,這可使更多冗餘模塊在 dedupe
過程當中被去掉。安裝模塊
node_modules
,並執行模塊中的生命週期函數(按照 preinstall、install、postinstall
的順序)。執行工程自身生命週期
install、postinstall、prepublish、prepare
的順序)。咱們要使用模塊A,而模塊A又依賴於模塊B,模塊B又依賴於模塊C和模塊D,npm能夠根據依賴關係,把全部依賴的包都下載下來並管理起來,而這種依賴又有不同的表現形式。
假設目前工程依賴 A, B, C 三個庫,而他們對某個庫 somelib
存在這樣的依賴關係:
A - somelib 1.4.x B - somelib 1.6.x C - somelib 1.6.x
若是要安裝 ABC 三個庫,那麼 somelib
會存在版本衝突。npm5+/yarn
會在實際安裝時,給三個庫分別下載各自依賴的 somelib
版本。假設 npm 先安裝了 A, 因爲 A 依賴 somelib 1.4.x
版本,那麼 自身依賴先安裝1.4.x
。再安裝 B, C 時,因爲 B, C 依賴的都不是 1.4.x
, 因而安裝完以後,關係就變成這個樣子了:
node_modules ├── A │ └── node_modules │ └── somelib 1.4.x ├── B │ └── node_modules │ └── somelib 1.6.x └── C └── node_modules └── somelib 1.6.x
這樣就是嵌套依賴。
很顯然這種方式很大的浪費了磁盤空間。
當關聯依賴中包括對某個軟件包的重複引用,在實際安裝時將盡可能避免重複的建立。
假設目前工程依賴 A, B, C 三個庫,而他們對某個庫 somelib
存在這樣的依賴關係:
A - somelib 1.4.x B - somelib 1.6.x C - somelib 1.6.x
若是要安裝 ABC 三個庫,那麼 somelib
會存在版本衝突。npm5+/yarn
會在實際安裝時,給三個庫分別下載各自依賴的 somelib
版本。假設 npm 先安裝了 A, 因爲 A 依賴 somelib 1.4.x
版本,那麼 1.4.x
會變成主版本。再安裝 B, C 時,因爲 B, C 依賴的都不是 1.4.x
, 因而安裝完以後,關係就變成這個樣子了:
node_modules ├── A ├── somelib 1.4.x ├── B │ └── node_modules │ └── somelib 1.6.x └── C └── node_modules └── somelib 1.6.x
這樣就是扁平依賴。
須要注意的是,明明 B, C 都依賴 1.6.x 版本,實際上
npm5+/yarn
卻要把這個版本保存兩次,這樣明顯是對磁盤空間的浪費。咱們把這種狀況就稱爲不徹底扁平的。目前這種狀況還沒法安全解決。
鎖文件是由包管理器自動生成的。它包含了重現所有的依賴源碼樹須要的全部信息、你的項目依賴中的全部信息,以及它們各自的版本。
如今值得強調的是,Yarn
使用了鎖文件,而 npm5
之前沒有默認鎖文件,npm5
以後加入了默認鎖文件功能。咱們會談到這種差異致使的一些後果。既然我已經向你介紹了包管理器這部分,如今咱們來討論依賴自己。
目前常見的兩種lock文件:
packahe-lock.json
是npm5以後默認生成的鎖文件yarn.lock
是yarn的鎖文件{ "name": "package-name", "version": "1.0.0", "lockfileVersion": 1, "dependencies": { "cacache": { "version": "9.2.6", "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==" }, "duplexify": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", "dependencies": { "end-of-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=" }, } } }
能夠看出來,package-lock.json把全部的包的依賴順序列出來,第一次出現的包名會提高到頂層,後面重複出現的將會放入被依賴包的node_modules當中。引發不徹底扁平化問題。
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 package-1@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" package-2@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/package-2/-/package-2-2.0.1.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 "^4.0.0" package-3@^3.0.0: version "3.1.9" resolved "https://registry.npmjs.org/package-3/-/package-3-3.1.9.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 "^4.5.0" package-4@^4.0.0, package-4@^4.5.0: version "4.6.3" resolved "https://registry.npmjs.org/package-4/-/package-4-2.6.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
顯然yarn.lock鎖文件把全部的依賴包都扁平化的展現了出來,對於同名包可是semver不兼容的做爲不一樣的字段放在了yarn.lock的同一級結構中。
在一個測試package工程裏面,安裝瞭如下三個包,安裝了react-router 3.2.1,另外安裝了react-router-dom 4.3.1 和react-router-native 4.3.0,這兩個都依賴"react-router": "^4.3.0",結果以下:
.
└── node_modules
├── react-router-dom4.3.1 │ └── react-router4.3.1 ├── react-router-native4.3.0 │ └── react-router4.3.1 └── react-router3.2.1
查看package-lock.json結果和最後node_modules安裝結果:
查看yarn.lock結果和最後node_modules安裝結果: