1. 程式人生 > 實用技巧 >VUE之npm知識點

VUE之npm知識點

一、npm安裝機制

若A 和 B 包同時依賴 C,C 這個包會被安裝在哪裡呢?C 的版本相同和版本不同時安裝會有什麼差異呢?package.json 中包的前後順序對於安裝時有什麼影響嗎?

1、A 和 B 同時依賴 C,這個包會被安裝在哪裡呢?

假如有 A 和 B 兩個包,兩個包都依賴 C 這個包

1)npm 2 會依次遞迴安裝 A 和 B 兩個包及其子依賴包到 node_modules 中。執行完畢後,我們會看到 ./node_modules 這層目錄只含有這兩個子目錄:

node_modules/ 
├─┬ A 
│ ├── C 
├─┬ B 
│ └── C 

2) npm 3 進行安裝的話,./node_modules

下的目錄將會包含三個子目錄:

node_modules/ 
├─┬ A 
├─┬ B 
├─┬ C 

差異主要來自2和3版本的安裝機制不同。 目前最新的 npm 版本是 npm 6,但 npm 2 到 npm 3 的版本變更中實現了目錄打平,與其他版本相比差別較大。

2、npm2和npm3安裝機制差異

1)npm 2 在安裝依賴包時,採用簡單的遞迴安裝方法。 執行 npm install 後,npm 根據 dependencies 和 devDependencies 屬性中指定的包來確定第一層依賴,npm 2 會根據第一層依賴的子依賴,遞迴安裝各個包到子依賴的 node_modules 中,直到子依賴不再依賴其他模組。執行完畢後,我們會看到 ./node_modules
這層目錄中包含有我們 package.json 檔案中所有的依賴包,而這些依賴包的子依賴包都安裝在了自己的 node_modules 中 ,形成類似於下面的依賴樹:

這樣的目錄有較為明顯的好處:

​ 1)層級結構非常明顯,可以清楚的在第一層的 node_modules 中看到我們安裝的所有包的子目錄;

​ 2)在已知自己所需包的名字以及版本號時,可以複製貼上相應的檔案到 node_modules 中,然後手動更改 package.json 中的配置;

​ 3)如果想要刪除某個包,只需要簡單的刪除 package.json 檔案中相應的某一行,然後刪除 node_modules 中該包的目錄;

但是這樣的層級結構也有較為明顯的缺陷,當我的 A,B,C 三個包中有相同的依賴 D 時,執行 npm install 後,D 會被重複下載三次,而隨著我們的專案越來越複雜,node_modules 中的依賴樹也會越來越複雜,像 D 這樣的包也會越來越多,造成了大量的冗餘;在 windows 系統中,甚至會因為目錄的層級太深導致檔案的路徑過長,觸發檔案路徑不能超過 280 個字元的錯誤;

​為了解決以上問題,npm 3 的 node_modules 目錄改成了更為扁平狀的層級結構,儘量把依賴以及依賴的依賴平鋪在 node_modules 資料夾下共享使用。

2)npm 3 會遍歷所有的節點,逐個將模組放在 node_modules 的第一層,當發現有重複模組時,則丟棄, 如果遇到某些依賴版本不相容的問題,則繼續採用 npm 2 的處理方式,前面的放在 node_modules 目錄中,後面的放在依賴樹中。 舉個例子: A,B,依賴 D(v 0.0.1),C 依賴 D(v 0.0.2):

但是 npm 3 會帶來一個新的問題:由於在執行npm install的時候,按照package.json裡依賴的順序依次解析,上圖如果 C 的順序在 A,B 的前邊,node_modules 樹則會改變,會出現下邊的情況,並未完全解決冗餘問題:

二、package-lock.json檔案

為什麼會有 package-lock.json 檔案呢?這個我們就要先從 package.json 檔案說起了。

1、package.json 的不足之處

npm install 執行後,會生成一個 node_modules 樹,在理想情況下, 希望對於同一個 package.json 總是生成完全相同 node_modules 樹。在某些情況下,確實如此。但在多數情況下,npm 無法做到這一點。有以下兩個原因:

1)某些依賴項自上次安裝以來,可能已釋出了新版本 。比如:A 包在團隊中第一個人安裝的時候是 1.0.5 版本,package.json 中的配置項為 A: '^1.0.5' ;團隊中第二個人把程式碼拉下來的時候,A 包的版本已經升級成了 1.0.8,根據 package.json 中的 semver-range version 規範,此時第二個人 npm install 後 A 的版本為 1.0.8; 可能會造成因為依賴版本不同而導致的 bug;

2)針對 1)中的問題,可能有的小夥伴會想,把 A 的版本號固定為 A: '1.0.5' 不就可以了嗎?但是這樣的做法其實並沒有解決問題, 比如 A 的某個依賴在第一個人下載的時候是 2.1.3 版本,但是第二個人下載的時候已經升級到了 2.2.5 版本,此時生成的 node_modules 樹依舊不完全相同 ,固定版本只是固定來自身的版本,依賴的版本無法固定。

2、針對 package.json 不足的解決方法

為了解決上述問題以及 npm 3 的問題,在 npm 5.0 版本後,npm install 後都會自動生成一個 package-lock.json 檔案 ,當包中有 package-lock.json 檔案時,npm install 執行時,如果 package.json 和 package-lock.json 中的版本相容,會根據 package-lock.json 中的版本下載;如果不相容,將會根據 package.json 的版本,更新 package-lock.json 中的版本,已保證 package-lock.json 中的版本相容 package.json。

package-lock的出現主要是來自yarn 的威脅?

    • 2016yarn 釋出
      • 支援 npmbower 倉庫
      • yarn.lock 能夠鎖定安裝的版本並提供確定性的依賴關係。不再 rm -rf node_modules
      • yarn install 花費的時間是 npm install 的一半(不使用快取的前提下)
      • 快取和離線模式使構建過程幾乎不花費時間
2016
npm
      釋出
shrinkwrap
      • 嘗試處理依賴項鎖定
      • 不幸的是,一些錯誤和超出其管理能力的承諾導致該工具的聲譽下降
2017
npm
      5 釋出
      • package-lock.json 是他們的新工具,shrinkwrap 被放在一邊
      • package-lock.json 開始與 yarns 鎖定檔案競爭
2018
npm ci
    釋出
    • 直接用 package-lock.json 構建程式碼
    • 沒有代價高昂的依賴項安全性分析和版本分析
    • 大大減少了在構建伺服器上的構建時間!

3、package-lock.json 檔案的結構

package-lock.json 檔案中的 name、version 與 package.json 中的 name、version 一樣,描述了當前包的名字和版本,dependencies 是一個物件,該物件和 node_modules 中的包結構一一對應,物件的 key 為包的名稱,值為包的一些描述資訊, 根據 package-lock-json官方文件,主要的結構如下:

  • version :包版本,即這個包當前安裝在 node_modules 中的版本
  • resolved :包具體的安裝來源
  • integrity :包 hash 值,驗證已安裝的軟體包是否被改動過、是否已失效
  • requires :對應子依賴的依賴,與子依賴的 package.jsondependencies 的依賴項相同
  • dependencies :結構和外層的 dependencies 結構相同,儲存安裝在子依賴 node_modules 中的依賴包

需要注意的是,並不是所有的子依賴都有 dependencies 屬性,只有子依賴的依賴和當前已安裝在根目錄的 node_modules 中的依賴衝突之後,才會有這個屬性。

4、package-lock.json 檔案的作用

  • 在團隊開發中,確保每個團隊成員安裝的依賴版本是一致的,確定一棵唯一的 node_modules 樹;
  • node_modules 目錄本身是不會被提交到程式碼庫的,但是 package-lock.json 可以提交到程式碼庫,如果開發人員想要回溯到某一天的目錄狀態,只需要把 package.json 和 package-lock.json這兩個檔案回退到那一天即可 。
  • 由於 package-lock.json 和 node_modules 中的依賴巢狀完全一致,可以更加清楚的瞭解樹的結構及其變化。
  • 在安裝時,npm 會比較 node_modules 已有的包,和 package-lock.json 進行比較,如果重複的話,就跳過安裝 ,從而優化了安裝的過程。

三、依賴的區別與使用場景

npm 目前支援以下幾類依賴包管理包括

  • dependencies
  • devDependencies
  • optionalDependencies 可選擇的依賴包
  • peerDependencies 同等依賴
  • bundledDependencies 捆綁依賴包

下面我們來看一下這幾種依賴的區別以及各自的應用場景:

1、dependencies

dependencies 是無論在開發環境還是在生產環境都必須使用的依賴,是我們最常用的依賴包管理物件,例如 React,Loadsh,Axios 等,通過 npm install XXX 下載的包都會預設安裝在 dependencies 物件中,也可以使用 npm install XXX --save 下載 dependencies 中的包;

2、devDependencies

devDependencies 是指可以在開發環境使用的依賴,例如 eslint,debug 等,通過 npm install packageName --save-dev 下載的包都會在 devDependencies 物件中;

dependencies 和 devDependencies 最大的區別是在打包執行時,執行 npm install 時預設會把所有依賴全部安裝,但是如果使用 npm install --production 時就只會安裝 dependencies 中的依賴,如果是 node 服務專案,就可以採用這樣的方式用於服務執行時安裝和打包,減少包大小。

3、optionalDependencies

optionalDependencies 指的是可以選擇的依賴,當你希望某些依賴即使下載失敗或者沒有找到時,專案依然可以正常執行或者 npm 繼續執行的時,就可以把這些依賴放在 optionalDependencies 物件中,但是 optionalDependencies 會覆蓋 dependencies 中的同名依賴包,所以不要把一個包同時寫進兩個物件中。

optionalDependencies 就像是我們的程式碼的一種保護機制一樣,如果包存在的話就走存在的邏輯,不存在的就走不存在的邏輯。

try { 
  var axios = require('axios') 
  var fooVersion = require('axios/package.json').version 
} catch (er) { 
  foo = null 
} 
// .. then later in your program .. 
if (foo) { 
  foo.doFooThings() 
} 

4、peerDependencies

peerDependencies 用於指定你當前的外掛相容的宿主必須要安裝的包的版本,這個是什麼意思呢?舉個例子:我們常用的 react 元件庫 [email protected]package.json 中的配置如下:

"peerDependencies": { 
  "react": ">=16.9.0", 
  "react-dom": ">=16.9.0" 
 }, 

假設我們建立了一個名為 project 的專案,在此專案中我們要使用 [email protected] 這個外掛,此時我們的專案就必須先安裝 React >= 16.9.0React-dom >= 16.9.0 的版本。 ​

在 npm 2 中,當我們下載 [email protected] 時,peerDependencies 中指定的依賴會隨著 [email protected] 一起被強制安裝,所以我們不需要在宿主專案的 package.json 檔案中指定 peerDependencies 中的依賴,但是在 npm 3 中,不會再強制安裝 peerDependencies 中所指定的包,而是通過警告的方式來提示我們,此時就需要手動在 package.json 檔案中手動新增依賴;

5、bundledDependencies

這個依賴項也可以記為 bundleDependencies,與其他幾種依賴項不同,他不是一個鍵值對的物件,而是一個數組,數組裡是包名的字串,例如:

{ 
  "name": "project", 
  "version": "1.0.0", 
  "bundleDependencies": [ 
    "axios",  
    "lodash" 
  ] 
} 

當使用 npm pack 的方式來打包時,上述的例子會生成一個 project-1.0.0.tgz 的檔案,在使用了 bundledDependencies 後,打包時會把 Axios 和 Lodash 這兩個依賴一起放入包中,之後有人使用 npm install project-1.0.0.tgz 下載包時,Axios 和 Lodash 這兩個依賴也會被安裝。需要注意的是安裝之後 Axios 和 Lodash 這兩個包的資訊在 dependencies 中,並且不包括版本資訊。

"bundleDependencies": [ 
    "axios", 
    "lodash" 
  ], 
  "dependencies": { 
    "axios": "*", 
    "lodash": "*" 
  }, 

如果我們使用常規的 npm publish 來發布的話,這個屬性是不會生效的,所以日常情況中使用的較少。

2018npm6 釋出