
作者:aprilandjan
依賴地獄
早期版本的的 npm (v2) 管理子產品依賴的方式并不複雜。它讀取每個子產品的依賴清單,并下載下傳比對版本的依賴子產品到該子產品目錄内的
node_modules
檔案夾下;如果該依賴又依賴了其他的子產品,會繼續下載下傳該依賴的依賴到該子產品目錄的
node_modules
檔案夾下——如此遞歸執行下去,最終形成一顆龐大的依賴樹。
例如,目前項目有依賴的子產品
A
,
B
,
A
又依賴于子產品
C
,
D
,
B
又依賴于子產品
C
,
E
,此時,項目的
node_modules
目錄結構如下:
root
└── node_modules
├── A
│ └── node_modules
│ ├── C
│ └── D
└── B
└── node_modules
├── C
└── E
可以想象,這樣做的确能盡量保證每個子產品自身的可用性。但是,當項目規模達到一定程度時,也會造成許多問題:
- 依賴樹的層級非常深。如果需要定位某依賴的依賴,很難找到該依賴的檔案所在(例如,如果想定位子產品
,就不得不先知道他在依賴樹中的位置);E
- 不同的依賴樹分支裡,可能有大量實際上是同樣版本的依賴(例如,
目錄下的 A
和 C
目錄下面的 B
如果版本一緻,實際上完全一樣);C
- 安裝時額外下載下傳或拷貝了大量重複的資源,并且實際上也占用了大量的硬碟空間資源等(例如,
子產品在依賴目錄中出現了兩次);C
- 安裝速度慢,甚至因為目錄層級太深導緻檔案路徑太長的緣故,在 windows 系統下删除
檔案夾也可能失敗!node_modules
正是因為這些問題的存在,彼時的
node_modules
又被叫做
依賴地獄(Dependency Hell)
。
依賴共享與沖突
在 npm v3 版本之後,npm 采用了更合理的方式去解決之前的依賴地域的問題。npm v3 嘗試把依賴以及依賴的依賴都盡量的平鋪在項目根目錄下的
node_modules
檔案夾下以共享使用;如果遇到因為需要的版本要求不一緻導緻沖突,沒辦法放在平鋪目錄下的,回退到 npm v2 的處理方式,在該子產品下的
node_modules
裡存放沖突的子產品。
例如,目前項目有依賴的子產品
,
,
依賴于子產品
,
,
又依賴于子產品
,
。注意,此時由于子產品
C
的兩個版本
和
被分别依賴,鑒于子產品在同一個
node_modules
目錄中是按照子產品名目錄存放,是以這兩個版本沒辦法同時平鋪在同一目錄,是以,其中一個版本的
C
子產品将會以 npm v2 的處理方式放入子
node_modules
目錄中。
那麼,應該是哪一個版本的
C
會被這樣處理呢?考慮以下操作時序:
- 在空目錄下,通過
先安裝 npm install \--save [email protected]
。由于它和它的依賴在 A
下都不會産生沖突,是以能夠直接平鋪的放入其中。此時目錄結構如下:node_modules
root
└── node_modules
├── [email protected]
├── [email protected]
└── [email protected]
- 繼續通過
安裝 npm install \--save [email protected]
。B
自身以及它的依賴 B
也沒有沖突,直接平鋪放入 E
下;但是 node_modules
的另一依賴 B
目錄下的 B
中。此時目錄結構如下:node_modules
root
└── node_modules
├── [email protected]
├── [email protected]
│ └── node_modules
│ └── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]
通過以上分析可知,如果先安裝
B
再安裝
A
,
将位于
A
目錄下的
node_modules
中。這說明:子產品的安裝順序可能影響
node_modules
内的檔案結構。
- 在上面的先
後 A
的情形下,繼續安裝依賴 B
的 F
中。此時目錄結構如下:node_modules
root
└── node_modules
├── [email protected]
├── [email protected]
│ └── node_modules
│ └── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]
└── node_modules
└── [email protected]
觀察發現,子產品
還是出現了備援。然而,假如安裝的順序是
B
A
F
,可以想象,将不會出現子產品備援的情況。這說明:子產品安裝順序可能影響
node_modules
内的檔案數量。
- 假設子產品
的新版本 A
npm install A@2
,将會發生以下操作:
此時的目錄結構如下:
- 移除子產品
- 移除子產品
- 添加子產品
- 在頂層
中安裝子產品 node_modules
root
└── node_modules
├── [email protected]
├── [email protected]
│ └── node_modules
│ └── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]
└── node_modules
└── [email protected]
可以發現,目錄中備援了多個
子產品!所幸 npm 提供了一個單獨的指令
npm dedupe
用以去掉類似情況下産生的備援拷貝。在 dedupe 之後,目錄結構如下:
root
└── node_modules
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]
順便提一句:
yarn
在安裝依賴時會自動執行
dedupe
操作:
$ yarn dedupe
yarn dedupe v1.17.3
error The dedupe command isn't necessary. `yarn install` will already dedupe.
info Visit https://yarnpkg.com/en/docs/cli/dedupe for documentation about this command.
可見 yarn 在設計時的确是抓住了很多細小的點去改善使用體驗。
參考
- https://npm.github.io/how-npm-works-docs/index.html