天天看點

運作npm install指令的時候會發生什麼?

摘要:我們日常在下載下傳第三方依賴的時候,都會用到一個指令npm install,那麼你知道,在運作這個指令的時候都會發生什麼嗎?

本文分享自華為雲社群《​​運作npm install指令的時候會發生什麼?​​》,作者: gentle_zhou。

npm(node package manager),是随同Node.js一起安裝的第三方包管理器;通過npm,我們可以安裝、共享、分發代碼,管理項目的依賴關系。

我們日常在下載下傳第三方依賴的時候,都會用到一個指令npm install,然後依賴包就會被安裝到node_modules目錄下;但是我們在運作這個指令的時候都會發生什麼呢?帶着好奇心,我去調研學習了一番。

大緻的流程是:npm install指令輸入 > 檢查node_modules目錄下是否存在指定的依賴 > 如果已經存在則不必重新安裝 > 若不存在,繼續下面的步驟 > 向 registry(本地電腦的.npmrc檔案裡有對應的配置位址)查詢子產品壓縮包的網址 > 下載下傳壓縮包,存放到根目錄裡的.npm目錄裡 > 解壓壓縮包到目前項目的node_modules目錄中。

運作npm install指令的時候會發生什麼?

(上面的圖檔就顯示了項目中依賴如果過多的尴尬:等待時間過長,下載下傳下來依賴過多導緻node_modules過大)

下面會介紹一下npm處理依賴的早前和目前的方式 以及 幾種不同的install指令下載下傳方式。

早期版本:遞歸

在npm早期版本裡,npm處理依賴的方式很粗暴簡單。它會嚴格按照根目錄下package.json檔案的結構以及各個子依賴包的package.json檔案的結構,遞歸地把依賴安裝到它們各自的node_modules目錄裡。這樣如果是個小項目,隻需要幾個依賴且這些依賴不會依賴别的依賴,那麼這樣的樹形結構就還算清晰明了(node_modules的結構與package.json裡的結構一一對應且層級結構明顯)。

但如果我們的項目是個大項目,裡面的依賴非常多(導緻嵌套層級非常深),且不同的層級可能會引用同一個依賴(導緻重複備援),就不是我們想要的情形了。

目前版本:扁平化

于是,為了解決以上遞歸管理依賴帶來的問題,npm在 3.X版本裡做了一次更新,引入了扁平化管理(dedupe)的方式。dedupe是dedeplicated的縮寫,即duplicates were remove,把重複的移除。

扁平化管理的思路就是首先周遊package.json檔案下dependencies和devDependencies字段裡的依賴,作為依賴樹的根節點;然後在每個根節點依賴下面都會有其依賴的依賴,作為其子節點;npm會開啟多程序從每個根節點開始逐漸往下尋找更深層次的節點。而package.json檔案下dependencies和devDependencies字段裡的依賴會被安裝在node_modules根目錄下。在周遊這些依賴的時候,如果發現有重複的依賴子產品(重複:子產品名相同且semantic version相容;這裡的相容,是指語義化版本都會有一段版本允許範圍,如果兩個依賴的版本号是在這個範圍交際裡就說明是相容;比如依賴X依賴于依賴Y@^1.0.0,而依賴Z依賴于依賴Y@^1.1.0,則Y@^1.1.0就是相容版本),就直接将其丢棄。

但是如果僅僅這樣,其實也有風險。在大項目中,很有可能會碰到依賴A依賴于依賴C-1.0版本,依賴B依賴于依賴C-2.0版本;而在執行npm install指令的時候,會按照package.json裡面的依賴順序依次解析,是以依賴C-1.0和依賴C-2.0的在檔案裡的放置順序會導緻Node_modules的依賴結構産生變化。而且為了讓開發者可以使用最新的依賴包,package.json檔案裡通常隻會鎖定大版本(即檔案裡依賴如果是^1.1.0版本,npm就會去倉庫中擷取符合1.x.x形式的最新版本),是以某些依賴包小版本更新後,也會造成依賴結構的改變。是以,為了解決npm install指令導緻的這種不确定問題,npm 5.x版本裡還新增了package-lock.json檔案。

package-lock.json檔案可以保證每次執行npm install後生成的node_modules目錄結構一定是完全相同的。下圖就是package-lock.json中其中一個依賴的資訊,有name-包名,version-包的版本号,dependencies-和node_modules中包結構一一對應的對象,resolved-包具體的安裝來源,integrity-包的hash值,requires-對應子依賴的依賴:

運作npm install指令的時候會發生什麼?

注:并不是所有的子依賴都有dependencies這個屬性,隻有子依賴的依賴和目前已安裝在根目錄的Node_modules中的依賴起了沖突之後,才會有這個屬性。

置于為何說package-lock.json 檔案 和 node_modules 目錄結構是一一對應的。還是舉剛剛前面提及的那個依賴沖突導緻依賴結構産生變化的例子,“依賴A依賴于依賴C-1.0版本,依賴B依賴于依賴C-2.0版本”,此時因為package-lock.json檔案的存在,我們會把依賴C-1.0版本安裝在依賴A的node_modules目錄下(對應依賴A在package.json檔案裡的dependencies屬性),依賴C-2.0版本安裝在根目錄下。這可以保障每次安裝生成的依賴目錄結構保持相同。

幾種不同的install指令下載下傳方式

  1. npm install xxx #(XXX是某依賴包)安裝依賴子產品至項目node_modules目錄下,不會修改package.json檔案裡的内容
  2. npm install -g xxx #安裝依賴子產品到全局(而不是項目node_modules目錄下),不會将該依賴子產品寫到package.json檔案裡的dependencies和devDependencies字段裡
  3. npm install --save xxx #安裝依賴子產品到項目node_modules目錄下,并将依賴寫入到package.json檔案裡的dependencies字段中;該依賴是開發和生産環境裡都需要的
  4. npm install --save-dev xxx #安裝依賴子產品到項目node_modules目錄下,并将依賴寫入到package.json檔案裡的devDependencies字段中