V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
asanelder
V2EX  ›  Node.js

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

  •  
  •   asanelder · 2020-06-02 20:53:13 +08:00 · 7155 次点击
    这是一个创建于 1394 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

    第 1 条附言  ·  2020-06-02 21:58:44 +08:00

    有些老铁没明白啊~~~

    同一个库多个版本的话,每个版本放到一个文件夹下不就行了,就像下面这样

    CleanShot2020-06-02at21.52.45

    还有,有些老铁的习惯很不好啊。。。总是喜欢转移话题。。。

    俺的问题是

       为什么不能有个中心依赖库
    

    有的老铁到话题转移到

       磁盘够不够用的问题。。。。
    

    磁盘够不够用和俺的问题无关吧。。。。

    48 条回复    2020-06-20 01:24:42 +08:00
    yujiff
        1
    yujiff  
       2020-06-02 20:57:31 +08:00
    好处,你可以想象得到吧?!
    dalibaxiaoliba
        2
    dalibaxiaoliba  
       2020-06-02 20:58:31 +08:00
    难到把所有项目的依赖放在一起会没有问题吗?现在磁盘空间又不是问题
    littleylv
        3
    littleylv  
       2020-06-02 20:59:32 +08:00
    版本问题。
    来人,上吐槽 node_modules 文件夹的图
    EPr2hh6LADQWqRVH
        4
    EPr2hh6LADQWqRVH  
       2020-06-02 20:59:56 +08:00
    虽然同一个库但版本可能不同,你明白吧
    FaiChou
        5
    FaiChou  
       2020-06-02 21:04:26 +08:00
    @littleylv #3 <img src="" alt="node_module is like a black hole" />
    pkuphy
        6
    pkuphy  
       2020-06-02 21:09:28 +08:00
    @FaiChou 已正确脑补
    forgottencoast
        7
    forgottencoast  
       2020-06-02 21:10:26 +08:00
    我记得 15 年的时候更恐怖,每一个库的依赖都放在自己库目录下的某个文件夹下(大概就是 node_modules ),
    这样导致了路径很长的循环依赖。
    我们在 Windows 上部署 CI 的时候,触发了路径超过 256 个字符的问题,当时我就震惊了,还能这样依赖的啊?
    记得不是太清楚了,如有错误,欢迎指正。
    abcbuzhiming
        8
    abcbuzhiming  
       2020-06-02 21:14:23 +08:00
    @dalibaxiaoliba 为啥总有人认为别人的电脑不是花钱买来的啊?你一个项目动则上 G 的依赖,还不是问题?
    gitjavascript
        9
    gitjavascript  
       2020-06-02 21:16:30 +08:00
    磁盘算个啥,再说了一个机器难道还会部署很多项目么
    abcbuzhiming
        10
    abcbuzhiming  
       2020-06-02 21:19:50 +08:00
    @avastms 之前也有人用这个做理由,说依赖同一个库但是版本不同。但是我后来想想发觉不对啊,你是可以依赖同一个库版本不同啊,你每个版本保存一份不就行了?好,问题来了,npm 有个天坑在于同一个包在本地无法多版本共存。于是它才不得不搞这种每个项目里都要塞一个依赖的做法
    TransAM
        11
    TransAM  
       2020-06-02 21:26:46 +08:00 via Android
    。。。谁告诉你不可以的?

    全局目录在 NODE_PATH 中配置,node 会在检查本地目录之后检查这个目录。
    renmu123
        12
    renmu123  
       2020-06-02 21:40:56 +08:00 via Android
    我觉得这个挺好的,版本管理很方便,如果各种库能少一点就更好了
    JJstyle
        13
    JJstyle  
       2020-06-02 21:45:20 +08:00 via iPhone
    多版本我只见过 homebrew 支持,虽然它不是依赖管理
    wangxiaoaer
        14
    wangxiaoaer  
       2020-06-02 21:49:48 +08:00 via Android
    @avastms maven 表示版本也成了问题?
    asanelder
        15
    asanelder  
    OP
       2020-06-02 21:53:43 +08:00
    @dalibaxiaoliba #2
    @avastms #4
    @gitjavascript #9

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

    ![CleanShot2020-06-02at21.52.45]( https://gitee.com/asanelder/pic_bed/raw/master/CleanShot%202020-06-02%20at%2021.52.45.png)
    xiangwan
        16
    xiangwan  
       2020-06-02 21:57:27 +08:00
    试试 lerna 或者 yarn workspace
    axihe
        17
    axihe  
       2020-06-02 22:01:13 +08:00
    楼主可以试一下 yarn2.0
    包管理这方面,虽然 npm 一直在努力和进步,但 yarn 还是走在前沿的。
    qingo
        18
    qingo  
       2020-06-02 22:08:37 +08:00 via Android   ❤️ 2
    java 还需要一些 wrapper 在项目中,rust 的 carge 用的更整洁,一步一步改过来 go mod 我不错,没有为什么,就是傻 B 而已
    asanelder
        19
    asanelder  
    OP
       2020-06-02 22:10:06 +08:00
    @qingo #18 哈哈,可以可以,等俺有精力,再了解了解其它的依赖管理
    alan0liang
        20
    alan0liang  
       2020-06-02 22:16:50 +08:00 via Android
    如 @axihe #17 所说,yarn 的 PnP 正解。
    直接原因应该是实现简单(因为 npm 是完全独立于 Node 的,如果需要像 lz 附言里的图那样,要不改 Node (很不好改),要不自己运行时注入一个 resolver)
    其实有一个很方便的地方,一个项目用完了可以放心的直接 rm -rf 掉,不用担心残留在本地的依赖项。(npm i -g 的包通常都是 cli,在项目里 require() 不到。)
    Jirajine
        21
    Jirajine  
       2020-06-02 22:18:50 +08:00 via Android
    我也觉得应该放一起,需要的时候再 vendor 出来
    asanelder
        22
    asanelder  
    OP
       2020-06-02 22:21:41 +08:00
    @alan0liang #20 没有残留这点确是是优点
    wusheng0
        23
    wusheng0  
       2020-06-02 22:26:32 +08:00 via Android
    @renmu123
    我也觉得这样挺不错,类似于 Python 虚拟环境
    thtznet
        24
    thtznet  
       2020-06-02 22:28:58 +08:00   ❤️ 2
    .net 体系的 NuGet 不知道高到哪里去,但是看到微软就政治不正确,我懂的。
    Austaras
        25
    Austaras  
       2020-06-02 22:31:43 +08:00
    别问,问就是上 berry

    不过大概是因为 npm 上的包不靠谱方便随时改吧,我就这么用过不少次
    asanelder
        26
    asanelder  
    OP
       2020-06-02 22:42:39 +08:00
    @thtznet #24 老铁真逗,不过因为不开发 windows 相关,所以不了解巨硬那一套哈,不过 vscode 让俺对微软好感增加不少。
    love
        27
    love  
       2020-06-02 23:08:35 +08:00
    主要是这么简单地做法成本也不高,除非你有大量的项目。
    另外又不是没有象你说的,比如 pnpm / yarn2 都是共享文件的。
    Mithril
        28
    Mithril  
       2020-06-02 23:14:15 +08:00
    @thtznet NuGet 真的是用过所有包管理里面最好用的。。。
    asanelder
        29
    asanelder  
    OP
       2020-06-02 23:33:34 +08:00
    @love #27 ok,俺了解了解 yarn2
    runze
        30
    runze  
       2020-06-03 00:23:56 +08:00   ❤️ 6
    因为最早 web 端的依赖就是放在项目目录下。

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

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

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

    为了兼容性,这种设计就这样保留到了现在。
    asanelder
        31
    asanelder  
    OP
       2020-06-03 00:37:11 +08:00
    @runze #30 有理有剧,让人信服!总算看到一个说的通的答案了!!!
    JayLin1011
        32
    JayLin1011  
       2020-06-03 00:54:13 +08:00
    你的问题设计者肯定考虑到了,但还是这样设计了,你想想这是为什么,就能得出结论了。
    siganushka
        33
    siganushka  
       2020-06-03 00:56:02 +08:00
    第三方库要考虑各种各样的情况,比如项目 A 依赖 “[email protected]“,项目 B 依赖 “[email protected]”,这时候就不能放在一起(实际情况比这负责的多),当然你可以说放在一块再按版本号子目录存放,但这一是增加了深度,二是它的作用随着“多个项目的公共依赖部分的减少而减少”,况且硬盘的成本是很低的,所以最终还是取舍的问题。
    nl101531
        34
    nl101531  
       2020-06-03 08:48:08 +08:00 via iPhone
    当初新建一个 hello world,下载了 1G 的依赖,惊了。。。
    redbuck
        35
    redbuck  
       2020-06-03 08:50:13 +08:00
    我觉得不是包放哪里的问题.

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

    用包的人不应该关心某个包依赖什么.下载依赖的时候,也不应该下载它的依赖
    jeeyong
        36
    jeeyong  
       2020-06-03 10:12:18 +08:00
    上古时代是这么干的. 比如 python..但是都感受到了独立管理包的好处.
    统一管理的时候会出现很多听起来不值得一提,但是发现->解决耗时不少的问题.
    runze
        37
    runze  
       2020-06-03 11:10:44 +08:00
    @redbuck #35 这个也有历史原因,比如网页中用了三个 jQuery 的插件,肯定不可能每个插件自带一份 jQuery 。
    现在也一样,一大堆 react-xxx 的库,也不能每个都自带 react 。
    linrz
        38
    linrz  
       2020-06-03 11:46:20 +08:00   ❤️ 1
    https://github.com/nodejs/node/issues/4584 早期也有人提出为什么不能像 maven 一样,或者自定义寻找依赖 resolve 的策略,但是回复是当时已经 lock 定稿了
    julyclyde
        39
    julyclyde  
       2020-06-03 12:13:01 +08:00
    感觉是“app 制度”而不是“env 制度”的
    所以 node 用户和 docker 爱好者高度重叠
    libook
        40
    libook  
       2020-06-03 12:48:13 +08:00   ❤️ 1
    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.
    asanelder
        41
    asanelder  
    OP
       2020-06-03 13:17:26 +08:00
    @libook #40 感谢回答,涨知识了
    asanelder
        42
    asanelder  
    OP
       2020-06-03 13:21:43 +08:00
    @linrz #38 老铁 nb,这都被你找到了
    readonly
        43
    readonly  
       2020-06-03 15:07:32 +08:00
    这个问题我也想过,当年因为 npm 都崇尚语义化版本号,这样方便升级,比如 package.json 里依赖了 lodash ^1.0.0 版本,那么执行 npm update 之后可以自动升级到 ^1.9.9 这样的版本上,只要是满足语义化版本的就可以升级。那么如果两个项目都写了 lodash ^1.0.0,我升级其中的一个,另一个也就 resolve 到 ^1.9.9 了,会导致不可预期的事情。但后面出了个 package-lock.json,这个文件里面就完整的记录了当前项目所用的版本甚至是包的下载路径,所以完全可以 resolve 到正确的版本上了,之所以不做应该就是大家习惯了,毕竟这样比较好向主子申请更好的电脑配置不是~
    linrz
        44
    linrz  
       2020-06-03 16:39:01 +08:00
    @asanelder 哈哈哈,以前水过一篇博客,对这块略有了解 https://linrz.me/2019/11/11/the-future-of-javascript-package-managment/
    LeslieWongH
        45
    LeslieWongH  
       2020-06-03 16:42:44 +08:00
    node 的最初开发者在 JSConf Eu 2018 上表示自己挺后悔在 node 中加个 node_module 文件夹的,而且引用还不是以.js 后缀结尾,直接模块名。种种开发 node 时让他觉得很羞耻的东西,让他决定在离开了 node 团队多年后,再去开发另外一个 V8 运行时——deno 。;)
    详见油管。10 things I regret about node.js -- Ryan Dahl
    cheny95
        46
    cheny95  
       2020-06-03 17:18:11 +08:00
    a132811
        47
    a132811  
       2020-06-06 16:11:08 +08:00
    一句话就是,node 是为了解决多版本冲突引入的这家伙。
    http://npm.github.io/how-npm-works-docs/npm3/how-npm3-works.html

    为了解决 node 这个坑(还有超级多的其它坑),于是有了 deno
    Kobayashi
        48
    Kobayashi  
       2020-06-20 01:24:42 +08:00 via Android
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3080 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 12:45 · PVG 20:45 · LAX 05:45 · JFK 08:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.