深挖 NPM 機制

使用NPM安裝的時候會常常出現包衝突(好比多個主模塊的子模塊版本不一致等),致使在開發過程當中會遇到各類或大或小的問題。全部在這會介紹如下內容:html

  1. NPM 主要安裝方式
  2. NPM 包信息查詢
  3. NPM 安裝機制(主要)

安裝&查詢命令

NPM 各類安裝方式node

  • npm install packageName[@next | @versionNumber]git

    • 在 node_modules 中沒有指定模塊時安裝,(不檢查~/.npm目錄)
  • npm install packageName --f | -- forcegithub

    • 一個模塊不論是否安裝過,npm都要 強制從新安裝
  • npm update packageNamenpm

    • 若是遠程版本較新、或者本地版本不存在時安裝

NPM 查詢服務json

  • NPM經過registry的查詢服務,從而知道每一個模塊的最新版本。
  • 能夠經過 npm view packageName [version] 查詢對映模塊的信息

NPM 安裝機制

輸入 npm install 命令並敲下回車後,會經歷以下幾個階段(以 npm 5.5.1 爲例):緩存

1. 執行工程自身 preinstall函數

當前 npm 工程若是定義了 preinstall 鉤子此時會被執行。post

2. 肯定首層依賴模塊code

首先須要作的是肯定工程中的首層依賴,也就是 dependenciesdevDependencies 屬性中直接指定的模塊(假設此時沒有添加 npm install 參數)。

工程自己是整棵依賴樹的根節點,每一個首層依賴模塊都是根節點下面的一棵子樹,npm 會開啓多進程從每一個首層依賴模塊開始逐步尋找更深層級的節點。

若是查詢node_modules目錄之中已經存在指定模塊,那麼再也不從新安裝

3. 獲取模塊

獲取模塊是一個遞歸的過程,分爲如下幾步:

  • 獲取模塊信息

    • 在下載一個模塊以前,首先要肯定其版本,這是由於 package.json 中每每是 semantic version(semver,語義化版本)
    • 此時若是版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有該模塊信息直接拿便可
    • 若是沒有則從倉庫獲取(向registry查詢)。如 packaeg.json 中某個包的版本是 ^1.1.0,npm 就會去倉庫中獲取符合 1.x.x 形式的最新版本。
  • 獲取模塊實體。

    • 上一步會獲取到模塊的壓縮包地址(resolved 字段),npm 會用此地址檢查本地緩存,緩存中有就直接拿,若是沒有則從倉庫下載。
  • 查找該模塊依賴

    • 若是有依賴則回到第1步,若是沒有則中止。

4. 模塊扁平化(dedupe)

一步獲取到的是一棵完整的依賴樹,其中可能包含大量重複模塊。好比 A 模塊依賴於 loadsh,B 模塊一樣依賴於 lodash。在 npm3 之前會嚴格按照依賴樹的結構進行安裝,所以會形成模塊冗餘。

npm3 版本 開始默認加入了一個 dedupe 的過程。它會遍歷全部節點,逐個將模塊放在根節點下面,也就是 node-modules 的第一層。當發現有重複模塊時,則將其丟棄。

這裏須要對重複模塊進行一個定義,它指的是模塊名相同且 semver 兼容。每一個 semver 都對應一段版本容許範圍,若是兩個模塊的版本容許範圍存在交集,那麼就能夠獲得一個兼容版本,而沒必要版本號徹底一致,這可使更多冗餘模塊在 dedupe 過程當中被去掉。

好比

  • node-modules 下 foo 模塊依賴 lodash@^1.0.0,bar 模塊依賴 lodash@^1.1.0,則 ^1.1.0 爲兼容版本。
  • 而當 foo 依賴 lodash@^2.0.0,bar 依賴 lodash@^1.1.0,則依據 semver 的規則,兩者不存在兼容版本。會將一個版本放在 node_modules 中,另外一個仍保留在依賴樹裏。

舉個例子,假設一個依賴樹本來是這樣:

node_modules
-- foo
---- lodash@version1

-- bar
---- lodash@version2

假設 version1 和 version2 是兼容版本,則通過 dedupe 會成爲下面的形式:

node_modules
-- foo

-- bar

-- lodash(保留的版本爲兼容版本)

假設 version1 和 version2 爲非兼容版本,則後面的版本保留在依賴樹中:

node_modules
-- foo
-- lodash@version1

-- bar
---- lodash@version2

5. 安裝模塊

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

6. 執行工程自身生命週期

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

最後一步是生成或更新版本描述文件,npm install 過程完成。

End

文章分享同步於: https://github.com/zhongmeizh...

參考

相關文章
相關標籤/搜索