大家好,我是若川。今天給大家推薦一篇關于 npm install 的好文。很快能看完。
點擊下方卡片關注我、加個星標
學習源碼整體架構系列、年度總結、JS基礎系列

前言
項目中執行npm install
發生了什么,眾所周知,執行npm install
時會在當前項目目錄的node_modules
中安裝依賴。并且將依賴項的層級關系保存在package-lock.json
中。那么依賴項的層級關系是怎么確認的?依賴項之間是否存在區別?有無package-lock.json
在安裝時有什么區別呢?筆者希望借以此文可以和讀者們一起厘清npm install
。
工欲善其事必先利其器,先一起來了解下npm install
。
npm-install
常用的命令包含(具體含義和執行效果會在下文說明)
npm install
npm install -P|--save-prod
npm install -D|--save-dev
npm install -O|--save-optional
npm install --no-save
aliases: npm i, npm add
npm install
這個命令會在項目路徑下安裝一個或多個依賴包,如果項目中存在package-lock
、npm-shrinkwrap
或yarn.lock
文件。那么安裝過程將就基于這些文件。其優先級為:
npm-shrinkwrap.json
package-lock.json
yarn.lock
其中npm-shrinkwrap.json
是npm 5
之前的依賴鎖定文件,其在npm 5
后被package-lock.json
替換。兩個文件功能類似,最大的不同是npm-shrinkwrap.json
需要執行npm shrinkwrap
來初始化生成,而package-lock.json
為自動生成。本文撰寫時npm
使用的版本為v6.14.5
,故下文所有樣例均在此版本下做交流討論。其他版本任何問題歡迎留言討論。yarn.lock
是yarn cli
的產物,不在此文章討論。
npm install
執行時會解析package.json
中定義的依賴關系,進而根據package.json
生成package-lock.json
依賴鎖定文件。package.json
中的依賴關系可以由開發人員自行定義,也可使用npm install [--optional]
來指定存儲位置,或使用npm install --no-save
不進行存儲。
自古習物先習源。package.json
中是那些在影響依賴樹呢?
package.json
package.json
文件是項目的描述文件,包括項目的name
、version
、description
等很多字段。但真正影響依賴樹的并不多,包括dependencies
、devDependencies
、peerDependencies
、 optionalDependencies
、bundledDependencies
。下面一起來看下他們區別和使用場景。
dependencies:主要依賴
dependencies
是主要依賴。也即必須依賴。由包名和版本區間映射成的簡單 json
對象組成。版本區間由一個或多個分隔符組成。是項目運行、打包的主要依賴。執行npm install
時會從上至下遞歸安裝dependencies
及其內部依賴。并將解析的依賴樹存儲在package-lock.json
中。會隨版本一起發布到npm
庫。是最重要的依賴節點。
執行npm install -P|--save-prod
會將指定包放入此節點。
常用的dependencies
如:axios
、fetch
等。
{"dependencies":?{"axios":?"^0.21.1","fetch":?"^1.1.0"}
}
devDependencies:開發依賴
devDependencies
是開發環境的依賴。其格式同dependencies
,開發人員本地執行npm install
時也會從上至下遞歸安裝devDependencies
及其內部依賴。并將解析的依賴樹存儲在package-lock.json
中。也會隨版本一起發布到npm
庫。但是不同的是。非開發人員通過項目安裝依賴時。也就是包安裝后出現在node_modules
中時,devDependencies
不會被安裝,也不會出現在項目的package-lock.json
中。
所以在開發時,dependencies
和devDependencies
區別并不大,但是作為依賴包被其他項目引入時,只有dependencies
中的依賴會被解析成依賴樹并下載。devDependencies
中的依賴會被忽略。
執行npm install -D|--save-dev
會將指定包放入此節點。
常用的devDependencies
如:karma
、mocha
、webpack
等
{"devDependencies":?{"karma":?"^1.3.0","mocha":?"^5.2.0","webpack":?"^1.13.1","webpack-dev-server":?"^1.14.1"}
}
peerDependencies:同等依賴
peerDependencies
是同等依賴。其格式同dependencies
。主要在開發包時使用,常規開發項目不建議使用。比如某些情況下開發人員為了表明在不同系統和運行環境下的兼容性,即非所有情況下都是必要的依賴。就會通過peerDependencies
來表明。peerDependencies
中更像是當前包所依賴是插件。peerDependencies
里面的依賴會隨版本一起發布,但是不會自動安裝,需要開發和使用人員手動安裝。(npm v7
下會默認安裝)
如eslint-plugin-prettier
會同等依賴eslint
和prettier
。即eslint-plugin-prettier
需要在eslint
和prettier
都安裝時才會生效。但是其自身并不會強制安裝eslint
和prettier
。而是會通過警告的方式提示開發者。
如果不想在未安裝peerDependencies
的情況下提示警告。可以通過peerDependenciesMeta
設置optional
為true
來關閉警告。
{"peerDependencies":?{"eslint":?">=5.0.0","prettier":?">=1.13.0"},"peerDependenciesMeta":?{"eslint":?{"optional":?true}}
}
optionalDependencies:可選依賴
optionalDependencies
是可選依賴。其格式同dependencies
。當需要某些依賴在安裝失敗時不會阻塞項目的運行和打包,就可以使用optionalDependencies
來定義。optionalDependencies
里面的依賴會隨版本一起發布,且會自動安裝。可以使用npm install --no-optional
命令來跳過安裝。
執行npm install -O|--save-optional
會將指定包放入此節點。
optionalDependencies
使用需要開發者在項目中作兼容。如:
try?{var?foo?=?require('foo')var?fooVersion?=?require('foo/package.json').version
}?catch?(er)?{foo?=?null
}if?(?notGoodFooVersion(fooVersion)?)?{foo?=?null
}//?..?then?later?in?your?program?..if?(foo)?{foo.doFooThings()
}
bundledDependencies:綁定依賴
bundledDependencies
是綁定依賴,其值是一個數組。在發包時定義綁定包。常規項目中使用的并不多。和npm install
的也并無關系,所以在此不做過多介紹。值得一提的是,使用bundleDependencies
也是可以的。
{"name":?"awesome-web-framework","version":?"1.0.0","bundledDependencies":?["renderized","super-streams"]
}
了解了package.json
中影響依賴樹的節點,那么接下來就是重頭戲npm install
登場了~
一、無依賴沖突
最簡單的場景莫過于此,當項目的package.json
的依賴及其子 依賴間沒有沖突時,即A依賴B、C、D。表示為A[B、C、D]。則依賴會平鋪在node_modules
下。即使有多個相同的依賴,只要版本不存在沖突,就都符合當前場景。
舉個例子。fetch-demo2
項目中值依賴fetch
這一個包。
{"name":?"fetch-demo2","version":?"1.0.0","description":?"","main":?"index.js","dependencies":?{"fetch":?"^1.1.0"},"devDependencies":?{},"scripts":?{"test":?"echo?\"Error:?no?test?specified\"?&&?exit?1"},"keywords":?[],"author":?"","license":?"ISC"
}
此時執行npm i
,會得到如下的目錄結構的node_modules
。

可得到依賴樹(根據package-lock.json
分析得出)

不難得出以下結論。當項目中的依賴無沖突時,項目依賴及其內部依賴會平鋪在一級node_modules
中。
二、項目頂級依賴存在沖突
頂級依賴即項目package.json
中的依賴。
當項目頂級依賴存在沖突時,會將頂級依賴放在node_modules
中的一級目錄下,沖突的包放在自己的node_modules
下。為了模擬這種場景,將biskviit@2.0.0
放在fetch-demo2
項目的頂級依賴上。
{"dependencies":?{"fetch":?"^1.1.0","biskviit":?"2.0.0"}
}
執行npm install
后查看node_modules
的目錄結構

分析依賴樹
可以看出在頂級依賴biskviit@2.0.0
和內部依賴biskviit@1.0.1
存在沖突時,頂級依賴會占據node_modules
的一級目錄,內部依賴則會存儲在其內部的node_modules
中
三、項目內部依賴存在沖突
當項目的內部依賴存在沖突時,會先檢測一級node_modules
是否存在依賴包,不存在則存儲,如果存在判斷是否有版本沖突,無沖突則使用一級node_modules
的依賴包,有沖突則存儲在自身的node_modules
中。還是舉例說明下。
一級node_modules
無沖突包
發布自定義新包conflict-lbywer@1.0.0
到npm
。其依賴為
{"dependencies":?{"biskviit":?"^2.0.0"}
}
將fetch-demo2
項目的頂級依賴改為
{"dependencies":?{"conflict-lbywer":?"1.0.0","fetch":?"^1.1.0"}
}
刪除node_modules
和package-lock.json
后,執行npm i
后,查看目錄結構

分析依賴樹

可以看出conflict-lbywer
所依賴的biskviit@2.0.0
和fetch
所依賴的biskviit@1.0.1
沖突時,在頂級依賴沒有biskviit
的情況下,將biskviit@2.0.0
安裝到了頂級依賴。
如果將頂級依賴中的conflict-lbywer
和fetch
更換順序呢,依賴包順序是否會發生變化,我們一起來研究。
修改頂級依賴
{"dependencies":?{"fetch":?"^1.1.0","conflict-lbywer":?"1.0.0"}
}
刪除node_modules
和package-lock.json
后,執行npm i
后,查看目錄結構

分析依賴樹

可以看出在調整conflict-lbywer
和fetch
順序后,目錄結構并無變化。所以可得出結論,在一級node_modules
不存在沖突包時,會將高版本的包放在一級node_modules
中。低版本的放到內部的node_modules
中。
一級node_modules
有沖突包
如果一級node_modules
有沖突包時,情況又會如何呢?
刪除頂級依賴中的conflict-lbywer
{"dependencies":?{"fetch":?"^1.1.0"}
}
刪除node_modules
和package-lock.json
后,執行npm i
后,查看目錄結構

分析依賴樹

頂級依賴添加conflict-lbywer@1.0.0
{"dependencies":?{"fetch":?"^1.1.0","conflict-lbywer":?"1.0.0"}
}
直接執行npm i
后,查看目錄結構

分析依賴樹

可以發現,在依賴樹無變化的情況下,node_modules
的目錄結構是不一樣的。所以可以得出結論,在一級node_moudles
已經存在依賴包的情況下,新安裝的依賴包如果存在沖突,會安裝到內部的node_modules
中。
四、存在package-lock.json
這種情況也很簡單,npm install
會完全按照package-lock.josn
的層級結構下載安裝依賴包
刪除node_moudles
后,執行npm install
直接執行npm install
后,查看目錄結構

分析依賴樹

可以發現,在存在package-lock.json
的情況下,node_modules
的目錄結構是穩定的。
結語
上文對執行npm install
時,一些常見的情況做了測試和分析,也給出了相應的結論。歡迎讀者們批評斧正。也歡迎打賞點贊哦~
參考文獻和鏈接
https://docs.npmjs.com/cli/v7/commands/npm-install
https://docs.npmjs.com/cli/v7/configuring-npm/package-json
https://docs.npmjs.com/cli/v7/configuring-npm/folders
最近組建了一個江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你進群。
今日話題
略。歡迎分享、收藏、點贊、在看我的公眾號文章~
一個愿景是幫助5年內前端人走向前列的公眾號
可加我個人微信?ruochuan12,長期交流學習
推薦閱讀
我在阿里招前端,我該怎么幫你(可進模擬面試群)
2年前端經驗,做的項目沒技術含量,怎么辦?
點擊上方卡片關注我、加個星標
·················?若川簡介?·················
你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》多篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,活躍在知乎@若川,掘金@若川。致力于分享前端開發經驗,愿景:幫助5年內前端人走向前列。