为什么 npm 要为每个项目单独安装一遍 node_modules?

做成全局的有什么得失?
关注者
480
被浏览
329,905

24 个回答

其实现在依赖动不动好多层,随便就上百个依赖包,安装一次要数分钟的情况,确实早有人提出各种改革方案(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的方式(当然这方法风险很高,只能作为临时性措施)。

因为很多底部的包的依赖关系不是简单的A依赖B,B依赖C,而是A(v2.0.0)依赖B(v0.33.5beta),B又依赖别的一些特定版本的包。这并不少见。导致了依赖变成了很“硬”的依赖,不只是依赖的包和层多,还是包的版本也有讲究。如果让多个项目共享一个部署文件系统,是有风险的,所以npm默认是./node_modules/的本地安装。作为部署的一般实践,也是倾向于环境之间充分隔离的。慢是npm小包化的副作用,确实是急需优化的。