package-lock.json和yarn.lock的包依賴區別

node包管理

包是一段能夠複用的代碼,這段代碼能夠從全局註冊表下載到開發者的本地環境。每一個包可能會,也可能不會依賴於別的包。簡單地說,包管理器是一段代碼,它可讓你管理依賴(你或者他人寫的外部代碼),你的項目須要這些依賴來正確運行。

爲啥咱們須要一個包管理工具呢?由於咱們在Node.js上開發時,會用到不少別人寫的JavaScript代碼。若是咱們要使用別人寫的某個包,每次都根據名稱搜索一下官方網站,下載代碼,解壓,再使用,很是繁瑣。node

更重要的是,若是咱們要使用模塊A,而模塊A又依賴於模塊B,模塊B又依賴於模塊C和模塊D,npm能夠根據依賴關係,把全部依賴的包都下載下來並管理起來。不然,靠咱們本身手動管理,確定既麻煩又容易出錯。react

因而一個集中管理的工具應運而生:數據庫

  1. 你們都把本身開發的模塊打包後放到npm官網上,若是要使用,直接經過npm安裝就能夠直接用,不用管代碼存在哪,應該從哪下載。
  2. Yarn 是爲了彌補npm 的一些缺陷[速度慢,穩定性高]而出現的。」

npm

npm 爲你和你的團隊打開了鏈接整個 JavaScript 天才世界的一扇大門。它是世界上最大的軟件註冊表,每星期大約有 30 億次的下載量,包含超過 600000 個 包( package) (即,代碼模塊)。來自各大洲的開源軟件開發者使用 npm 互相分享和借鑑。包的結構使您可以輕鬆跟蹤依賴項和版本。

下面是關於 npm 的快速介紹:npm 由三個獨立的部分組成:npm

  • 網站
    網站 是開發者查找包(package)、設置參數以及管理 npm 使用體驗的主要途徑。
  • 註冊表(registry
    註冊表 是一個巨大的數據庫,保存了每一個包(package)的信息。
  • 命令行工具 (CLI)
    CLI 經過命令行或終端運行。開發者經過 CLI 與 npm 打交道。

yarn

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.jsonyarn.lock文件,並且它是默認建立的。除了常規信息以外, yarn.lock文件還包含要安裝的內容的校驗和,以確保使用的庫的版本相同。

yarn的優化主要體如今:緩存

  1. 速度快 :安全

    • 並行安裝:不管 npm 仍是 Yarn 在執行包的安裝時,都會執行一系列任務。npm 是按照隊列執行每一個 package,也就是說必需要等到當前 package 安裝完成以後,才能繼續後面的安裝。而 Yarn 是同步執行全部任務,提升了性能。
    • 離線模式:若是以前已經安裝過一個軟件包,用Yarn再次安裝時之間從緩存中獲取,就不用像npm那樣再從網絡下載了。
  2. 安裝版本統一:爲了防止拉取到不一樣的版本,Yarn 有一個鎖定文件 (lock file) 記錄了被確切安裝上的模塊的版本號。每次只要新增了一個模塊,Yarn 就會建立(或更新)yarn.lock 這個文件。這麼作就保證了,每一次拉取同一個項目依賴時,使用的都是同樣的模塊版本。
  3. 更好的語義化: yarn改變了一些npm命令的名稱,好比 yarn add/remove,感受上比 npm 本來的 install/uninstall 要更清晰。

node包的安裝

  1. 執行工程自身 preinstall網絡

    • 當前 npm 工程若是定義了 preinstall 鉤子此時會被執行。
  2. 肯定首層依賴react-router

    • 模塊首先須要作的是肯定工程中的首層依賴,也就是 dependenciesdevDependencies 屬性中直接指定的模塊(假設此時沒有添加 npm install 參數)。工程自己是整棵依賴樹的根節點,每一個首層依賴模塊都是根節點下面的一棵子樹,npm 會開啓多進程從每一個首層依賴模塊開始逐步尋找更深層級的節點。
  3. 獲取模塊dom

    • 獲取模塊是一個遞歸的過程,分爲如下幾步:
    • 獲取模塊信息。在下載一個模塊以前,首先要肯定其版本,這是由於 package.json 中每每是 semantic version(semver,語義化版本)。此時若是版本描述文件(npm-shrinkwrap.jsonpackage-lock.json)中有該模塊信息直接拿便可,若是沒有則從倉庫獲取。如 packaeg.json 中某個包的版本是 ^1.1.0,npm 就會去倉庫中獲取符合 1.x.x 形式的最新版本。
    • 獲取模塊實體。上一步會獲取到模塊的壓縮包地址(resolved 字段),npm 會用此地址檢查本地緩存,緩存中有就直接拿,若是沒有則從倉庫下載。
    • 查找該模塊依賴,若是有依賴則回到第1步,若是沒有則中止。
  4. 模塊扁平化(dedupe

    • 上一步獲取到的是一棵完整的依賴樹,其中可能包含大量重複模塊。好比 A 模塊依賴於 loadsh,B 模塊一樣依賴於 lodash。在 npm3 之前會嚴格按照依賴樹的結構進行安裝,所以會形成模塊冗餘。yarn和從 npm5 開始默認加入了一個 dedupe 的過程。它會遍歷全部節點,逐個將模塊放在根節點下面,也就是 node-modules 的第一層。當發現有重複模塊時,則將其丟棄。這裏須要對重複模塊進行一個定義,它指的是模塊名相同且 semver 兼容。每一個 semver 都對應一段版本容許範圍,若是兩個模塊的版本容許範圍存在交集,那麼就能夠獲得一個兼容版本,而沒必要版本號徹底一致,這可使更多冗餘模塊在 dedupe 過程當中被去掉。
  5. 安裝模塊

    • 這一步將會更新工程中的node_modules,並執行模塊中的生命週期函數(按照 preinstall、install、postinstall 的順序)。
  6. 執行工程自身生命週期

    • 當前 npm 工程若是定義了鉤子此時會被執行(按照 install、postinstall、prepublish、prepare 的順序)。

包依賴關係

咱們要使用模塊A,而模塊A又依賴於模塊B,模塊B又依賴於模塊C和模塊D,npm能夠根據依賴關係,把全部依賴的包都下載下來並管理起來,而這種依賴又有不同的表現形式。

  1. 嵌套依賴
  2. 扁平依賴

嵌套依賴

假設目前工程依賴 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 卻要把這個版本保存兩次,這樣明顯是對磁盤空間的浪費。咱們把這種狀況就稱爲不徹底扁平的。目前這種狀況還沒法安全解決。

lock文件

鎖文件是由包管理器自動生成的。它包含了重現所有的依賴源碼樹須要的全部信息、你的項目依賴中的全部信息,以及它們各自的版本。

如今值得強調的是,Yarn 使用了鎖文件,而 npm5之前沒有默認鎖文件,npm5以後加入了默認鎖文件功能。咱們會談到這種差異致使的一些後果。既然我已經向你介紹了包管理器這部分,如今咱們來討論依賴自己。

目前常見的兩種lock文件:

  1. packahe-lock.json 是npm5以後默認生成的鎖文件
  2. yarn.lock 是yarn的鎖文件

packahe-lock.json解析

{
  "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當中。引發不徹底扁平化問題。

yarn.lock解析

# 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安裝結果:

clipboard.png
圖片描述

查看yarn.lock結果和最後node_modules安裝結果:

圖片描述

圖片描述

lock官方介紹
理解 NPM 5 中的 lock 文件
知乎問答1
知乎問答2

相關文章
相關標籤/搜索