如今的前端開發幾乎都離不開nodejs的包管理器npm
,好比前端在搭建本地開發服務以及打包編譯前端代碼等都會用到。在前端開發過程當中,常常用到npm install
來安裝所需的依賴,至於其中的技術細節未作過多的理解,下面就來講說node包管理器npm。javascript
使用npm來管理nodejs的包依賴,須要在項目根目錄下提供一個package.json
文件,其中與包依賴相關的字段有:html
其中,你們應該都知道:經過命令npm install --save $package
來安裝運行時依賴的模塊,npm install --save-dev $package
來安裝本地開發時所依賴的模塊。前端
須要指出的是,經過npm install
來安裝包依賴時,dependencies
和devDependencies
是有區別的,具體以下:java
package.json
的目錄下執行npm installnpm install $package
package.json
的目錄下執行npm install;可是,執行 npm install --production
該命令是不會安裝devDependencies指定的包的npm install $package --dev
時會安裝對應的依賴包; 可是, 執行npm install $package
時是不會安裝devDependencies指定的包的因此:node
咱們常常在經過
npm install $package
來安裝一個依賴包時,npm只會安裝該依賴包的package.json文件中的dependencies
所指定的依賴包,devDependencies
是不會被安裝的。webpack
另外,經過npm install $package
在安裝指定依賴包時,該包package.json中的peerDependencies
所指定的依賴包及其版本,則是對依賴該$package包的宿主環境的要求。git
若宿主環境沒有安裝對應的包或者安裝的版本不知足要求,npm在安裝過程當中給出錯誤警告。github
npm在安裝依賴包時,會將依賴包下載到當前的node_modules目錄中。每一個包安裝事後都會有本身的node_modules嗎?這又涉及到不一樣版本的npm其對包依賴的目錄組織結構有所不一樣。web
npm2依賴安裝的時候比較簡單直接,直接按照包依賴的樹形結構下載填充本地目錄結構,也就是說每一個包都會將該包的依賴組織到當前包所在的node_modules目錄中。npm
npm2這樣設計的緣由多是引用文章[2]的一句話:
由於 npm 設計的初衷就是考慮到了包依賴的版本錯綜複雜的關係,同一個包由於被依賴的關係緣由會出現多個版本,簡單地填充結構保證了不管是安裝仍是刪除都會有統一的行爲和結構。
這樣,一個項目App 裏模塊 A 和 C 都依賴 B,不管被依賴的 B 是不是同一個版本,都會生成下面的目錄結構:
很明顯:
這種依賴的組織結構,雖然簡單的實現多版本兼容,可是可能形成目錄結構嵌套比較深,而且極可能形成相同模塊的大量冗餘問題。
npm3則對依賴安裝進行了改造,採用」扁平結構「的思路來組織依賴包的目錄結構。具體的就是npm install
時:
按照 package.json 裏依賴的順序依次解析,遇到新的包就把它放在第一級目錄,後面若是遇到一級目錄已經存在的包,會先判斷版本,若是版本同樣則忽略,不然會按照 npm2 的方式依次掛在依賴包目錄下。
以上面的例子看一下對比結果:
這樣,npm3+採用這種扁平結構部分的解決了npm2的痛點。
爲何說是部分解決呢,npm3+並無完美解決npm2中的問題,在某些狀況下甚至會退化到npm2的行爲。
例如項目App裏依賴模塊A、C、D、E, 其中A、C、D依賴模塊B v2.0, E依賴模塊B v1.0,生成的npm3結構以下
能夠看到B、C、D模塊下包含了各自依賴的B v2.0,存在代碼冗餘的狀況。
那麼是否能夠解決這代碼冗餘問題呢,在E模塊依賴的模塊B升級到V2.0前提下,咱們能夠經過npm dedupe
把全部二級的依賴模塊B v2.0重定向到一級模塊B下,以下圖所示:
上面說到了npm2與npm3依賴包組織結構的不一樣;那麼如何找到對應的依賴包呢,例如項目訪問webapck依賴包:
const webapck = require('webpack')
那麼nodejs是怎麼找到webpack模塊的呢,這就是涉及到依賴包的路徑查找問題。具體以下:
若是傳遞給
require()
的參數不是nodejs的核心模塊,也不是以/
、../
或./
開頭,
那麼nodejs會嘗試從當前模塊所在目錄開始,嘗試在它的 node_modules 文件夾里加載相應模塊,根據模塊的package.json來加載對應的js文件;若是沒有找到,那麼就再向上一級目錄移動,直到文件系統的根目錄爲止。
例如,假設在/home/wonyun/projects/foo.js
文件裏調用了 require('bar.js')
,那麼 nodejs 查找其位置的順序依次爲:
/home/wonyun/projects/node_modules/bar.js
/home/wonyun/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
若果追蹤到文件系統的根目錄也沒有找到對應的依賴,那麼nodejs就會找不到對應模塊的報錯。
聲明一下:以上圖片使用文獻[2]的圖片加以說明。
一、【npm】詳解npm的模塊安裝機制
二、npm2 npm3 yarn 的故事
三、npm&&npmscript&&gulp&&webpack
四、What's the difference between dependencies, devDependencies and peerDependencies in npm package.json file?