Nodejs 为什么 node 项目的依赖都要放在 node_modules 下,而不整个中心依赖库?

Nodejs 为什么 node 项目的依赖都要放在 node_modules 下,而不整个中心依赖库?

比如有 2 两个 node 项目,依赖同一个库,那么每个项目中的 node_modules 都有一份。这不是重复下载和浪费磁盘空间么?

java 就不这样,java 一般都是放在 repository 下面(我指的是使用 maven).

当然,我知道可以 node install -g 。

但是我了解到的实践中,都是每个项目一份 node_modules 。

难到把所有项目的依赖放在一起会有问题?


49 回复

好处,你可以想象得到吧?!


难到把所有项目的依赖放在一起会没有问题吗?现在磁盘空间又不是问题

版本问题。
来人,上吐槽 node_modules 文件夹的图

虽然同一个库但版本可能不同,你明白吧

#3 <img src="" alt=“node_module is like a black hole” />

已正确脑补

我记得 15 年的时候更恐怖,每一个库的依赖都放在自己库目录下的某个文件夹下(大概就是 node_modules ),
这样导致了路径很长的循环依赖。
我们在 Windows 上部署 CI 的时候,触发了路径超过 256 个字符的问题,当时我就震惊了,还能这样依赖的啊?
记得不是太清楚了,如有错误,欢迎指正。

为啥总有人认为别人的电脑不是花钱买来的啊?你一个项目动则上 G 的依赖,还不是问题?

磁盘算个啥,再说了一个机器难道还会部署很多项目么

之前也有人用这个做理由,说依赖同一个库但是版本不同。但是我后来想想发觉不对啊,你是可以依赖同一个库版本不同啊,你每个版本保存一份不就行了?好,问题来了,npm 有个天坑在于同一个包在本地无法多版本共存。于是它才不得不搞这种每个项目里都要塞一个依赖的做法

。。。谁告诉你不可以的?

全局目录在 NODE_PATH 中配置,node 会在检查本地目录之后检查这个目录。

我觉得这个挺好的,版本管理很方便,如果各种库能少一点就更好了

多版本我只见过 homebrew 支持,虽然它不是依赖管理

maven 表示版本也成了问题?

#2
#4
#9

java 就可以同一个库多版本并存啊,每个版本一个文件 夹不就行了

![CleanShot2020-06-02at21.52.45]( https://gitee.com/asanelder/pic_bed/raw/master/CleanShot%202020-06-02%20at%2021.52.45.png)

试试 lerna 或者 yarn workspace

楼主可以试一下 yarn2.0
包管理这方面,虽然 npm 一直在努力和进步,但 yarn 还是走在前沿的。

java 还需要一些 wrapper 在项目中,rust 的 carge 用的更整洁,一步一步改过来 go mod 我不错,没有为什么,就是傻 B 而已

#18 哈哈,可以可以,等俺有精力,再了解了解其它的依赖管理

如 #17 所说,yarn 的 PnP 正解。
直接原因应该是实现简单(因为 npm 是完全独立于 Node 的,如果需要像 lz 附言里的图那样,要不改 Node (很不好改),要不自己运行时注入一个 resolver)
其实有一个很方便的地方,一个项目用完了可以放心的直接 rm -rf 掉,不用担心残留在本地的依赖项。(npm i -g 的包通常都是 cli,在项目里 require() 不到。)

我也觉得应该放一起,需要的时候再 vendor 出来

#20 没有残留这点确是是优点


我也觉得这样挺不错,类似于 Python 虚拟环境

.net 体系的 NuGet 不知道高到哪里去,但是看到微软就政治不正确,我懂的。

别问,问就是上 berry

不过大概是因为 npm 上的包不靠谱方便随时改吧,我就这么用过不少次

#24 老铁真逗,不过因为不开发 windows 相关,所以不了解巨硬那一套哈,不过 vscode 让俺对微软好感增加不少。

主要是这么简单地做法成本也不高,除非你有大量的项目。
另外又不是没有象你说的,比如 pnpm / yarn2 都是共享文件的。

NuGet 真的是用过所有包管理里面最好用的。。。

#27 ok,俺了解了解 yarn2

因为最早 web 端的依赖就是放在项目目录下。

比如你的页面引用了 jQuery,要么把它放在 CDN 上,要么放在项目的 web 目录下,如果放在系统级的中心仓库,那么要怎么通过 script 标签引用它呢?

后来有了 web 端的包管理工具,例如 bower,也是放在项目目录下的 bower_components,这样可以通过 /bower_components/xxx/xxx.js 这样子引用。

node_modules 只不过是继承了这种做法而已,在刚开始的时候,node 端的工具还不多,node 仓库里大部分还是前端库,有不少人拿 node_modules 当作 bower_components 使用,&lt;script src="/node_modules/jquery/dist/jquery.js"&gt; 这样子。

为了兼容性,这种设计就这样保留到了现在。

#30 有理有剧,让人信服!总算看到一个说的通的答案了!!!

你的问题设计者肯定考虑到了,但还是这样设计了,你想想这是为什么,就能得出结论了。

第三方库要考虑各种各样的情况,比如项目 A 依赖 “[email protected]“,项目 B 依赖 “[email protected]”,这时候就不能放在一起(实际情况比这负责的多),当然你可以说放在一块再按版本号子目录存放,但这一是增加了深度,二是它的作用随着“多个项目的公共依赖部分的减少而减少”,况且硬盘的成本是很低的,所以最终还是取舍的问题。

当初新建一个 hello world,下载了 1G 的依赖,惊了。。。

我觉得不是包放哪里的问题.

而是包发布的时候就应该是完整可运行的.它依赖什么,发布的时候就应该打包好.变成它代码的一部分.

用包的人不应该关心某个包依赖什么.下载依赖的时候,也不应该下载它的依赖

上古时代是这么干的. 比如 python…但是都感受到了独立管理包的好处.
统一管理的时候会出现很多听起来不值得一提,但是发现->解决耗时不少的问题.

#35 这个也有历史原因,比如网页中用了三个 jQuery 的插件,肯定不可能每个插件自带一份 jQuery 。
现在也一样,一大堆 react-xxx 的库,也不能每个都自带 react 。

https://github.com/nodejs/node/issues/4584 早期也有人提出为什么不能像 maven 一样,或者自定义寻找依赖 resolve 的策略,但是回复是当时已经 lock 定稿了

感觉是“app 制度”而不是“env 制度”的
所以 node 用户和 docker 爱好者高度重叠

  1. 把所有项目依赖放在一起会有问题?答:看需求,贴合需求就没问题,与需求矛盾就有问题。
    2. 每个项目中的 node_modules 都有一份依赖是不是 Node 的机制?答:不是,这是 npm 的机制,Node 并没有强制要求每个项目的依赖放在项目自己的 node_modules 里。
    3. Node 是否支持把所有项目依赖放在一起?答:支持,npm 只是一种包管理方案,Node 的包管理器有很多种,比如 pnpm 就是采用了把所有项目依赖放在一起的方案。
    4. node_module 是否支持你把依赖放在多个项目的公共父目录下供所有子项目使用?答:支持,实际上如果当前目录下找不到依赖,Node 会尝试到上一级目录来查找依赖,一直到根目录: https://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders
    > If the module identifier passed to require() is not a core module, and does not begin with ‘/’, ‘…/’, or ‘./’, then Node.js starts at the parent directory of the current module, and adds /node_modules, and attempts to load the module from that location. Node.js will not append node_modules to a path already ending in node_modules.
    > If it is not found there, then it moves to the parent directory, and so on, until the root of the file system is reached.

#40 感谢回答,涨知识了

#38 老铁 nb,这都被你找到了

这个问题我也想过,当年因为 npm 都崇尚语义化版本号,这样方便升级,比如 package.json 里依赖了 lodash ^1.0.0 版本,那么执行 npm update 之后可以自动升级到 ^1.9.9 这样的版本上,只要是满足语义化版本的就可以升级。那么如果两个项目都写了 lodash ^1.0.0,我升级其中的一个,另一个也就 resolve 到 ^1.9.9 了,会导致不可预期的事情。但后面出了个 package-lock.json,这个文件里面就完整的记录了当前项目所用的版本甚至是包的下载路径,所以完全可以 resolve 到正确的版本上了,之所以不做应该就是大家习惯了,毕竟这样比较好向主子申请更好的电脑配置不是~

哈哈哈,以前水过一篇博客,对这块略有了解 https://linrz.me/2019/11/11/the-future-of-javascript-package-managment/

node 的最初开发者在 JSConf Eu 2018 上表示自己挺后悔在 node 中加个 node_module 文件夹的,而且引用还不是以.js 后缀结尾,直接模块名。种种开发 node 时让他觉得很羞耻的东西,让他决定在离开了 node 团队多年后,再去开发另外一个 V8 运行时——deno 。;)
详见油管。10 things I regret about node.js – Ryan Dahl

<iframe src="https://www.youtube.com/embed/M3BM9TB-8yA" class="embedded_video" allowfullscreen="" type="text/html" id="ytplayer" frameborder="0"></iframe>

一句话就是,node 是为了解决多版本冲突引入的这家伙。
http://npm.github.io/how-npm-works-docs/npm3/how-npm3-works.html

为了解决 node 这个坑(还有超级多的其它坑),于是有了 deno

在Node.js项目中,依赖之所以要放在node_modules目录下,而不是整个中心依赖库,主要有以下几个原因:

  1. 模块化与封装

    • Node.js采用模块化设计,每个模块(或包)都可以独立开发、测试和部署。
    • node_modules目录作为项目的本地依赖库,确保了每个项目拥有自己独立的依赖环境,避免了不同项目间的依赖冲突。
  2. 版本控制

    • 每个依赖包在node_modules下都有自己的版本信息,这有助于确保项目使用的依赖版本是稳定和兼容的。
    • npm和Yarn等包管理器会生成package-lock.jsonyarn.lock文件,锁定依赖的具体版本,进一步保证版本的一致性。
  3. 便于开发与协作

    • 开发者只需在本地环境中运行npm installyarn命令,即可根据package.json文件自动安装项目所需的所有依赖。
    • 这使得团队协作更加高效,每个开发者都可以在自己的本地环境中快速搭建起项目所需的依赖环境。

综上所述,将Node.js项目的依赖放在node_modules目录下,是实现模块化、版本控制和高效开发协作的重要机制。

回到顶部