Nodejs 请教,为什么大部分框架,工具,脚手架等都需要在你的项目目录下重复的安装 node_modules?

发布于 1周前 作者 caililin 来自 nodejs/Nestjs

Nodejs 请教,为什么大部分框架,工具,脚手架等都需要在你的项目目录下重复的安装 node_modules?
NPM 明明是有所谓全局依赖存储路径的啊,为什么每个项目还需要单独一份 node_modules,非常的占空间。为啥不能像 maven,Gradle,Nuget 那样,只存一份完事了。是历史习惯,还是不得不这么做?

54 回复

node.js 就是因为这个垃圾包死掉的
没人负责,没人统筹,一包垃圾,不死才怪


Node.js 创始人 Ryan Dahl 都嫌弃 Node 的槽点,搞新的开源项目 Deno 去了

为什么我觉得还挺好?
绝对不会出现诡异问题,所有依赖都自己掌控

你用 maven 能出现什么诡异的问题?

你还别说,我记得上次哪听说 Python 都要学着这么干了。
理由无非是全局的包和各个项目下的依赖不一样,大家现在都用 pyenv,venv,pipenv 之类的在不同项目中加载不同的,所以好像是 pip 还是啥的要搞一套类似 Node 这样的玩法,没指定走全局,当前目录下有的话走当前目录…还能方便打包 blahblah,毕竟你看 electron 那带的东西。

道理是对的,但我总觉得哪里还是有点不对劲。好像可以有更省事的方法来着。

你这个项目用的 vue 是 1.x 的那个项目的是 2.x 的,如果只存一份,会怎样?
两份都存的话,后续的 yarn 之类的就这么操作的

我就喜欢现在这样

也没那么绝对~~py 的 env 也不是完全独立~有些用到系统依赖的就 gg 了

三行 js,npm install 一下比 jdk 都大

其实,最骚的是有些包自己去 Github 下东西,更骚的是这东西还可能已经被删了,你想想一下你用 mvn install 一下下载一个 jar,然后这个 jar 还可能去 github 下载一个 exe,最后你发现 release 页 exe 被作者删除了

这个难道不能在全局路径下 vue1.x 和 2.x 共存吗?这没什么吧,大部分包管理工具都是这么干的,多版本共存,每个版本只留一份

cnpm 就修改了逻辑,所有的模块都是全局一个版本,项目下面的都只是全局的软链。

对,这个我就比较不能忍…

作为一个包,居然还有外部不可控的依赖……

文件体积大小就算了,毕竟打包后的项目的体积还可控,但万一人家外部依赖的挂了。。。

开发都进行不下去了好吗!!

#13 而且这货不走你设置的那些缓存的,调 CI 跟吃了一坨屎一样难受

存在外部非 npm 依赖的包可以不要用,npm 一开始的设计允许外部依赖还是一个挺 open 的,不然现在 github package 也没办法用这个 cli。这是给你用公司私有源或者紧急情况下自己 release fork 的包等等解决方案。

这么多人吐槽不用全局包,但 node 社区完全是有的啊,用 yarn2 不就完事了,什么 “node.js 就是因为这个死的” 都出来了,真的有了解过这个社区吗。

而且说到局部包和版本控制这个问题也和社区本身有关,因为社区的包太多了而且非常活跃,版本爆炸依赖复杂,所以这也是一个当时看起来比较好的解决方式,虽然以如今的眼光看有一些弊端,如分析依赖对比可用版本更新需要花费很多时间、磁盘读写时间长、没有过期危险的依赖警告 (有所改善了),但社区也一直在推陈出新,包括楼上说的缓存,yarn 就做的不错,还有很多 CI 也开始了专业级别的 nodejs 缓存等等,以我的视角看,在众多语言社区的包管理上,虽然算不上好,但绝不至于让语言死掉。

我觉得没什么问题,就是有些 node modules 莫名失踪,才愁人。。。

npm5 体验很好了吧,顺便为什么这贴压根没提 java 都能引来 ptsd 患者……

我一直很郁闷的是 node_modules 动不动就以万计的文件,真的是硬件配置限制了我的想像

关键还是水货包太多了

这事情,基本就是:理想丰满,现实骨感,背影还行,转身要命。

这点没得洗,依赖管理非常不友好.尤其是没有package-lock的时候.
然后前端本来也变化多段…跑旧项目的时候别提有多糟心了.

这是最简单粗暴的解决依赖版本问题的办法。

对的,我最开始用 4,那时候还没有 package-lock,后来一次升级到 8 有了 package-lock,感觉世界都晴朗了。

还有很多相同的功能的不同模块,被引用到项目中。(轮子太多)
比如判断是否是对象,有 isObj, is-object, isObject 等等,不同的作者可能使用的库都不一样,可能都被下载到我们的 node_modules

版本不同,并不是所有人都爱做向下兼容

Yarn 会在 c 盘缓存,发现的时候 6 个 G 了。都存一个地方没改目录,恐怕 C 盘马上就爆掉了

请问一下,npm5 到底能不能实现共享的依赖?如何实现?

版本不同没关系啊,我的意思是,难道不能同版本只留一份吗?一个统一的存放目录,xx 框架 1.0,2.0,3.0 各一份,其它项目想引用哪个版本就引用哪个版本啊,为啥非要像现在这样,一个项目下下一次 xx 框架 1.0。换个项目又下一次 1.0 ?

只考虑 npm 的话,你可以不使用 npm install,通过手工做软链之类的来把项目依赖链接到你指定的位置,以项目(repo)角度来说,有一些工具可以实现依赖共享比如 lerna(昨天刚好看过一下相关讨论),另外,webpack 本身也可以指定 resolve 的位置。所以说方法有很多,但是共享依赖在很多 corner case 里会带来很多问题,所以 npm 本身的策略是,不共享


我只看到一个提及 jar, 也并没有说 java 如何,那个是在拿 maven 类比,
题主本身就提到了 maven, 然后请百度 maven 是什么, 看看到底谁有 ptsd

maven 采用的方式就是 本地一个存储仓库,存放所有下载下来的不同版本的依赖包, 除了编译 build 时会复制项目用到的依赖外,所有的项目里都不会存在完全相同的依赖包.

而且 npm 本身问题一直不少, 比如 namespace


还是有明白人啊

我觉得某些 java 用户被迫写 js 之后患上了 ptsd 有问题吗? maven 是 java 的仓库管理工具需要你来科普给我吗? npm 本身也有 cache 需要我写明白吗?现在 npm 有 /pkgname,你不知道,就代表没有吗?

现在应该是可以了吧,同一版本只下载一次

多占点地方我无所谓,不出现冲突省事就行.
我倒是一直想,npm 下载下来的包如果是一个 webpack 打包好的独立 js 就好了

项目单独安装依赖挺好的呀,python venv 就是这样,毕竟不同项目使用的同一 package 可能是不同版本,所以要单独安装。


如果嫌 npm 安装慢那是自己网络问题,可以用国内淘宝源:npm install --registry=https://registry.npm.taobao.org

或者用 openwrt 路由器彻底解决网络问题。

争吵没有意义,上来扣帽子无聊
现在我想知道的是,npm 的这类历史遗留问题还是否会进行改进
一种轮子如果不吸取别的地方的精华,只能是自己玩的欢
#32

再补充一个 https://www.zhihu.com/question/41409670?sort=created
Java 在 20 年前就解决这个问题了,然后后面抄的语言没有一个完全抄对的

不会改进
而且其他包管理器早晚会在每个项目本地搞一个依赖目录的(滑稽

npm 在这里试图解决的问题是
当你的项目 P 存在依赖 A、B、C、D 时,
P 依赖 D1
A 依赖 D2
B 依赖 D3
C 依赖 D4
这种问题是要考虑的


这里能优化的是两个场景
- 一个是已经被 yarn 和 npm 做掉的,一个项目内对一个依赖有多次依赖的时候,会尽量 resolve 到同一个版本(其实新版 npm 做的比 yarn 好,能让 node_modules 更扁平,但是带来了更多的问题(滑稽
- 另一个是楼主说的 不同的项目对一个依赖要求的版本相同的时候,能不能只存一份

后面这个有点尴尬,依赖是往上不断找 node_modules 的,按道理是可以做一个 “根 node_modules”,但是
P1 P2 依赖 D1
P3 P4 依赖 D2
要怎么处理呢?
symlink 也不行,webpack 的 resolve.symlinks 可以使用文件的真实路径,如果这么搞,算是 break change,非常僵硬。


或者就等,真 break change,PnP 上线吧,完全由 js 来控制 resolve 依赖的行为,新的依赖布局不需要过多考虑兼容性,能有更多的操作空间。

不黑不吹,也不说无关的事。

有人喜欢放在统一的目录里,有的人喜欢相互隔离,事实上确实不同应用场景下有不同的需求,不能说哪一种绝对好;工具只能提供一种默认方案(两种就不叫默认了),但工具也可以提供足够的灵活性来让人们自助解决需求差异。

Node 对与 node_modules 的搜索是有一个规则的,规则不满足需求也可以用 NODE_PATH 机制来指定,可以结合一些 Npm 的指令以及自动化脚本来 Hack 出一个基于 Npm 的集中包管理机制。
Npm 并不是 Node modules 的唯一管理工具,pnpm 就可以提供“ One version of a package is saved only ever once on a disk.”的特性。

实现整个系统环境或用户环境有一个共享的包目录所遇到的问题,肯定比实现每个项目下有一个包安装目录遇到的问题要复杂。举个例子,要删除一个包或一个版本的包的时候,肯定是基于一个项目的需求来删除的,但共享目录是所有项目都在用的,那仅当只有这一个项目依赖这个包的时候才可以删除这个包,这有点类似垃圾回收机制。类似的问题肯定是可以有解决方案的,只不过也一定是需要额外的成本的,将一个项目的问题封锁在项目目录内,相当于是将包管理的问题降维,这可能是 Npm 一直在用这个方案的一个历史原因。

对于 Node 服务开发来说,依赖包占用空间多的情况较少,问题主要集中在网页开发方面,这个其实不是 Npm 的锅,主要是现在网页开发应用的语言太多,每种都需要特定的编译程序的支持,比如 JSX、Vue、Sass、TypeScript 等等,空间占用大,编译时间长,最终生成的网站大小并不大(否则就会有流量问题和加载性能问题了)。

所以建议根据项目的具体情况来选择包管理方案。

嗯。。。 我觉得硬盘不值钱呐。 那就用最简单的办法 node_modules 嵌套处理包依赖吧。
如果 node_modules 名字是 a 开头如 a_node_modules 就更好了~ 还能防 rm -rf …
逃~~

#15 这不是我说不用就不用的问题,我记得很清楚,当时是一个 sass 的某个版本,智障的去下载 Github 的东西,然后 404 ;国内线上机房的网络大家都懂,本身链接 Github 就慢的要死,实际 Relese 版本还被作者删了;像 sass 这种东西生产级的项目你没法说不用就不用,就我目前做 Devops 来说,直接让前端换这种级别的东西有点过了

CI 现在都没法处理这个 node_modules,你说你 cache 整个目录,本身就贼鸡儿大就不说了,每个项目还一个; CI cache 时还会做压缩处理,里面全是海量小文件,有时候都可能出现文件名超出文件系统限制;你 cache npm、yarn 的哪个 cache 目录吧,有些包还特么自己联网下东西;就算不联网下载的你 cache 住了,你会发现 npm install 仍然会进行一个很长时间的编译动作,极大拖慢 CI build 时间

node-sass,老大难了,但是算作 npm 的问题是不合适的

每个项目都有个 node_modules,多了之后在 win10 上会出现我的电脑显示“处理中”,导致 explorer 卡住,只能重启解决,node_modules 里面小文件太多了

等一下朋友,我搜索资料过,说 NPM 现在已经能把依赖树扁平化了,只要依赖树扁平化,为何不能一个版本只存一份呢。你说的那些问题,如果 NPM 的扁平化依赖方式能处理,那么,只存一个位置——不管它放在哪里,都是毫无问题的。除非 NPM 的依赖扁平化也解决不了你说的:
P1 P2 依赖 D1
P3 P4 依赖 D2
你说的这个问题其实就是依赖不同版本,为了避免冲突,不得不用本地存放的方式来生成一个嵌套树

所以现在我感觉大家的回答很矛盾。我想问的是能不能“全部依赖放在集中的位置,一个版本只留一份”。而大家回答的是因为历史原因,容易出现依赖一个库但是版本不同的原因,为了应对这个问题,必须使用嵌套树存放,好像嵌套树才是 npm 必须使用本地目录存放依赖的根本原因。但是又有人回复我说 NPM 已经解决了这个依赖嵌套树,一级套一级问题,实现了依赖树扁平化——但是只要依赖树扁平化了,那实际就是一个版本只放一份吧。这等于说 NPM 依赖处理是可以实现集中存放的。还是说 NPM 的依赖扁平化并不是适用所有场景的?前套树在某些情况下还是得存在

空间换便利

我查的资料是部分解决了嵌套问题,比如 A,B 同时依赖 C1,那 C1 不需要嵌套,如果 A 依赖 C1,而 B 依赖 C2,则还是以前的老办法

接手过一个 node.js 写的 H5 小项目,整个项目代码大小 400MB ……

本来想凑合着用的,后来实在受不了,改用 PHP 重写,世界都清净了!!!

node 这样的玩法,我看不长久啊……(个人看法,不喜勿喷)

> 只要依赖树扁平化,为何不能一个版本只存一份呢
是啊,这是两个场景
- 同一个项目
- 多个项目

对于第一种,各种管理器会尽可能的 resolve 一个依赖到同一个版本,如果可以,可能就会直接放置在项目的 node_modules 下面,哪怕它是依赖的依赖,不能的那些,还是会和原先一样,黑洞式放置

这里说到了 “尽可能的 resolve 一个依赖到同一个版本”,如果本身就是写死了不同的版本,那么包管理器也是无能为力的。

对于第二种,历史包袱也在,版本冲突问题无法妥善解决。


上面说的问题都是,需要考虑兼容性的,如果是自己的项目,没有兼容性考虑,是完全可以实现 所有项目公用依赖 的。



扁平化,不是说完全扁平化,上面说了,是"尽可能的 resolve 一个依赖到同一个版本",本身声明依赖的版本就不同,(包管理器)是没有办法处理这种情况的。


最后,希望大家可以看下 PnP,以及社区围绕新的依赖布局的讨论,这些问题都是被讨论很多次了。

建议 node 学 php 一样,大量常用函数直接内置,或者推动一两个标准“库”成为官方标准,我记得是有这样的库的。

现在大量 node 的项目引用成千上万个库,这些库又引用其他库,真当每个开发者的电脑都是顶配?真当开发者的时间、网络、硬盘不用钱?

有段时间确实想试试直接用 node 解决后端的,试了一下果断放弃,node 这玩意还是留给前端精英们去玩。反正学多一门语言( php、python、lua ……)又不难。

虽说是存一份儿,1.x 和 2.x 不能分开两个文件夹存嘛,各项目用配置的版本号各取所需

Yarn Plug’n’Play 和 pnpm 可以了解一下,他们都是在试图解决这个问题

老铁,你是没用过 python 吧? python 就是全局安装,后来用了个 virtualenv 来解决全局安装的问题

每个项目单独安装一下,你觉得不方便是因为你的网速太烂,这个没办法

但单独安装,显然是有极大的好处的

node_modules 就是智障- -

在Node.js生态系统中,大部分框架、工具和脚手架要求在项目目录下重复安装node_modules的原因主要涉及到依赖管理、版本控制和环境隔离。

  1. 依赖管理:每个项目可能有不同的依赖和依赖版本。通过在项目目录中安装node_modules,可以确保每个项目使用正确的依赖版本,避免版本冲突。

  2. 版本控制package.jsonpackage-lock.json(或yarn.lock)文件记录了项目所需的依赖及其版本。通过运行npm installyarn install,可以精确地重现项目所需的依赖树,确保开发、测试和生产环境的一致性。

  3. 环境隔离:将依赖安装在项目目录中,可以避免全局依赖带来的潜在冲突和不确定性。每个项目都有自己独立的依赖环境,这有助于维护项目的稳定性和可维护性。

下面是一个简单的示例,展示了如何在项目目录中安装依赖:

# 创建一个新的Node.js项目目录
mkdir my-node-project
cd my-node-project

# 初始化一个新的package.json文件
npm init -y

# 安装一个依赖(例如express)
npm install express

# 此时,my-node-project目录下会生成一个node_modules文件夹,包含express及其依赖

综上所述,将node_modules安装在项目目录中是一种最佳实践,它有助于确保项目的依赖一致性、稳定性和可维护性。

回到顶部