為什麼 npm 要為每個項目單獨安裝一遍 node_modules?

做成全局的有什麼得失?


其實現在依賴動不動好多層,隨便就上百個依賴包,安裝一次要數分鐘的情況,確實早有人提出各種改革方案(npm/node/node NG等項目repo里都有人開issue)。不過因為npm的功能現在也非常複雜(比如shrinkwrap、bundle這類很可能你沒在用,但是用了的人很依賴的特性),還有許多配套工具都依賴這結構。要在保持兼容的前提下改基礎結構是非常困難的。npm3打平目錄這個事情就幹了好久,還被許多人詬病留了好多bug。(再舉個例子,node_modules這個名字實在有點挫,現在npm自己都說是通用的包管理,並不局限於node,然而這名字現在都不可能改。)

需要注意的是最早的node.js/npm實際上是全局的。包括現在還兼容NODE_PATH。但是不能支持全局多版本。當初改成局部可能就是為了支持分別依賴不同版本的包。(所謂的deps hell,關於這一點,其實不同語言、平台的各種選擇可以單獨扯一大通,這裡不展開了。只能說npm算是很有特色的一種。)

本來就算是全局的也並不是不能做成支持分別依賴不同版本,只是resolve path的代碼會比較複雜和不直觀。所以一開始選擇了最簡單明了了做法。如果今天從頭來過,恐怕就不是現在這個樣子。當然我這屬於事後諸葛亮,站著說話不腰疼。

最後講兩點局部的好處,聊以自慰:

1. 局部的有一個好處是移動/複製/打包項目比較簡單。

2. 另外一個好處是你可以直接改local依賴包的代碼(畢竟js就是源碼)而不怕影響其他項目。雖然不可當常規做法,但是有時候調試問題確實挺方便的。另外甚至有人用直接在local的node_modules上做monkypatch的方式(當然這方法風險很高,只能作為臨時性措施)。


因為每個項目依賴的包版本不一樣。聽說過DLL hell沒?

從管理的角度來講,局部依賴有利於減少對運行環境的配置工作,無論對開發還是對發布/後繼維護升級都是大大減輕了工作量。

全局依賴的唯一好處就是省了硬碟空間。這種省毫無意義。首先如果你要為幾十幾百兆的硬碟空間斤斤計較,那麼也許你已經窮得不適合做開發。其次如果需要支持全局多版本也省不了多少。至於有人說的,每次npm install時間太長,我認為這也不是個事。npm install又不是天天搞,而且只是第一次全新checkout的時候比較慢,以後都是增量更新。實在嫌慢(比如因為防火牆的原因),可以把node_modules一起提交到git里去。


因為很多底部的包的依賴關係不是簡單的A依賴B,B依賴C,而是A(v2.0.0)依賴B(v0.33.5beta),B又依賴別的一些特定版本的包。這並不少見。導致了依賴變成了很「硬」的依賴,不只是依賴的包和層多,還是包的版本也有講究。如果讓多個項目共享一個部署文件系統,是有風險的,所以npm默認是./node_modules/的本地安裝。作為部署的一般實踐,也是傾向於環境之間充分隔離的。慢是npm小包化的副作用,確實是急需優化的。


瀉藥。

為題主點個贊,思考得不錯。

其實我覺得完全可以做成全局的,依賴模塊都裝到公共目錄,每個項目在 npm install 時用符號連接把每個模塊對應的版本目錄連過來,或者乾脆就在 require() 時去全局的模塊目錄里去找,這樣也不麻煩。

實際上我團隊就包了這樣一個命令,安裝時是全局安裝,項目 init 時符號連接過來,很省時間和空間。

但 npm 沒有這麼做,我覺得一是在一開始沒考慮到,後面也就不好改了。

實際上就連 node_modules 模塊多層嵌套導致路徑過長的問題,也是一開始設計時沒考慮周全,到了 npm3 才改。


Java在20年前就解決這個問題了,然後後面抄的語言沒有一個完全抄對的


npm3 就是把依賴拍平的,因為之前會產生非常深的嵌套導致目錄刪不掉,甚至爆 linux 的 PATH_MAX。


其實這個問題也不能怪npm的那群人,根本原因是js語言本身的問題,因為語言本身的lib過少,所以想要實現一個功能就難免會引用別人造好的輪子,於是就會自然的產生了引用鏈。

至於做成全局,你難以保證每個人在現有環境的情況下去兼容這個項目,這也是像Docker這些容器存在的意義,保證環境局部可控。


NPM就是屎,NodeJS就是翔,兩者珠聯璧合,互相打臉,啪啪啪


npm這點簡直太坑了,弄個項目都整一堆的node_module,不能全局共享,並且node_module下面還有node_module,層層疊下去,不知何時才是頭。設計真是爛


做成全局的完全可行,但要解決的問題瞬間多了一倍:除了項目依賴又多了系統已安裝包的管理。

但是這些包放哪裡又有什麼關係呢?其影響體現在多人開發的時候。

做成全局的,考慮一下這種情況,20人開發同一項目,這些人電腦上已經安裝的全局包種類和版本各不相同。因為他們有人還在開發其他項目,你需要在不影響他們現有版本的情況下讓他們的環境兼容這個項目。好不容易搞好了,哪個包更新了,你得通知所有人也更新一遍(因為要照顧到現有版本,每個人的更新步驟也可能不一樣,那麼得你自己親自出馬)。開發過程中還可能有人會用錯版本,造成的bug很難查。

(以上吐槽來自於原來在Linux下碰到的系統動態庫依賴問題,有段時間不斷來實習生,我一個碼農硬生生被逼成了裝機工。後來拋棄了所有系統動態庫改成了局部載入。)

做成局部的話就用版本控制的工具更新一下就可以,不同的項目互不影響。


說到底還是因為 node 包的API不穩定,上下不兼容的情況太多了。


npm cache是全局的呀。。。局部複製一遍方便複製項目全部文件?


例如版本問題,比如對同一個庫,兩個不同的東西,一個顯式要求必須是1.3才能正常使用,另外一個要求2.0以上


你有感知是因為國內訪問npm有點慢……從最終結果上看,npm有效粗暴地解決了對不同版本依賴的問題……當然有更好的解決方案,但是這年頭誰在乎那點磁碟空間啊。如果切到阿里的鏡像,包的下載速度也是非常快的。


copy/paste是js一開始就有的壞習慣,node當然也就繼承了。。。


引入一個類似MAVEN本地倉庫的概念不可以嗎? 在從倉庫分發到本地開發環境. 解決了一次下載多次使用的問題.


這點的確做的不好 最近有個項目用了不少包 依賴複雜 本身能跑 但是npm shrinkwrap無法生成 連它自己都2b了

最後還是花點時間清理了一些舊的包 最後才生成正確的shrinkwarp 讓我最崩潰的是rm node_modules這個folder居然要好幾分鐘。。。

3打平是個不錯的做法


1.依賴版本問題

2.PaaS友善,容器友善,減少運維的工作量


maven不這麼做,導致開發和運行階段各種問題,解決maven包衝突要花費大量精力


1、不管是不是全局,Deps Hell總是存在的,總是要有一定規則去解決的。

2、版本更新和解決衝突機制需要一個全局唯一的identity來搭建, 而不一定是庫文件本身

3、解決防火牆問題是一個重要的易用性因素,要不很多人玩不轉。借用遊戲的說法就是,認為玩不懂的玩家是煞筆,這樣的策劃才是煞筆

我覺得npm需要進化適應中國環境。


推薦閱讀:

TAG:Nodejs | npm | 包管理器 |