運行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目錄中。
(上面的圖片就顯示了項目中依賴如果過多的尷尬:等待時間過長,下載下來依賴過多導致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-對應子依賴的依賴:
註:並不是所有的子依賴都有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版本安裝在根目錄下。這可以保障每次安裝生成的依賴目錄結構保持相同。
package-lock.json 文件還有個優點,就是它會快取每個包的具體版本和下載鏈接,在後期再去install的時候,就不需要再去遠程倉庫進行查詢操作了,減少了大量網路請求。
幾種不同的install命令下載方式
- npm install xxx #(XXX是某依賴包)安裝依賴模組至項目node_modules目錄下,不會修改package.json文件里的內容
- npm install -g xxx #安裝依賴模組到全局(而不是項目node_modules目錄下),不會將該依賴模組寫到package.json文件里的dependencies和devDependencies欄位里
- npm install –save xxx #安裝依賴模組到項目node_modules目錄下,並將依賴寫入到package.json文件里的dependencies欄位中;該依賴是開發和生產環境里都需要的
- npm install –save-dev xxx #安裝依賴模組到項目node_modules目錄下,並將依賴寫入到package.json文件里的devDependencies欄位中