这样做的好处很明显
这样可以完美的避免每个项目都需要 npm install 问题, 同一个包必须在每个项目中都拷贝一份
肯定有人说现在磁盘不值钱的, 所有依赖也就几百 MB, 不值得这种优化
但是有没有想过每次开发时每次 npm install 几分钟, 出错之后 windows 下删除又是几分钟(大量小文件)对开发效率的影响.
我的问题是:
1
hantsy 2020-01-05 15:32:33 +08:00
最新的 Yarn,NPM 好像都是全局的吧,只是项目下有一份 Copy。
|
2
lihongjie0209 OP @hantsy #1 应该说是全局缓存吧, 安装的时候还是会复制缓存到项目下
|
3
realkenshinji 2020-01-05 15:50:42 +08:00 via iPhone
缺点就是同一个包可能在不同项目下的版本不同,所以分项目来安装依赖是必须的,我想这也是 python 下项目为什么要搞 venv 的原因吧
|
4
charlie21 2020-01-05 15:59:42 +08:00
明知道有大量小文件的操作,为什么要在 win 下进行呢?
|
5
lihongjie0209 OP @charlie21 #4 我没必要因为一个包管理器换操作系统和硬件吧
|
6
lihongjie0209 OP @realkenshinji #3 在 Java 中, 比如说你要使用 packagex 1.1 版本的包, 那么就会在启动时指定 "maven 本地仓库 /packagex/1.1", 当你在另一个项目中使用 2.1 版本的时候, 就会指定 "maven 本地仓库 /packagex/2.1", 所以不会冲突
|
7
watzds 2020-01-05 16:25:04 +08:00 via Android
Java 要运行,也会打包复制那些吧
|
8
watzds 2020-01-05 16:25:20 +08:00 via Android
Java 要服务器上运行,也会打包复制那些吧
|
9
agagega 2020-01-05 16:27:25 +08:00 via iPhone
这不是 Node 一直吹的优点之一吗?像 RubyGems 这些工具就是像楼主说的一样按版本安装在系统目录的。
另外,install -g 不行? |
10
lihongjie0209 OP @watzds #8 类似 webpack, 是可以配置的. 不过这里讨论的是在开发阶段, 如何避免这种不必要的依赖安装
|
11
lihongjie0209 OP @agagega #9 估计全局安装问题很大, 因为设计上之前就没考虑过这么使用
|
12
houzhenhong 2020-01-05 16:50:58 +08:00 2
pnpm 和 yarn 的 pnp 不知道符不符合你的要求
|
13
lihongjie0209 OP @houzhenhong #12 我去看看, 感谢回复
|
14
leafdream 2020-01-05 17:35:38 +08:00
你可以 npm i -g
|
15
gitjavascript 2020-01-05 17:42:29 +08:00
难道你们在服务器上 install 么
|
16
zappos 2020-01-05 17:46:27 +08:00 via Android
@realkenshinji venv 还不如 docker 方便。。。而且 venv 只解决 python 的问题,docker 可以解决任何语言的这类问题。
|
17
jybox 2020-01-05 17:46:27 +08:00
最大的好处就是每个包可以对自己使用的依赖版本有充分的控制。Node.js 里一个项目(由若干个包构成)里可以同时使用同一个包的不同版本,而不必像 Java (我不是很了解 Java,但我听说是这样)在一个项目中,只能使用一个包的特定版本,如果不同的包依赖了同一个包的不同版本,就可能出现无法解决的依赖冲突。
作为一个 Node.js 开发者,我是非常认可这种做法的,我认为这种设计所解决的问题是要比带来的麻烦多的。 |
18
zappos 2020-01-05 17:52:45 +08:00 via Android
( 1 )可以选择安装在局部和全局,( 2 )局部优先
npm 都实现了啊,没问题啊 只不过有时候 require 全局的包需要配置个路径,我记得,配置了就好了 |
19
optional 2020-01-05 18:00:03 +08:00 via iPhone
这是优点。。不是缺点。 全局安装还要管权限之类的问题
|
20
lihongjie0209 OP @optional #19 不需要, 类似 maven 全局安装可以指定任何目录, 每一个项目都可以配置自己的全局目录, 极端情况可以做到类似 npm 的一个项目一个本地仓库
|
21
lihongjie0209 OP @optional #19 如果这是优点的话, 那么 pnpm 和 yarn 的 pnp 出现的意义是什么
|
22
zhbhun 2020-01-05 18:40:58 +08:00
同意 jybox 的看法,现在项目依赖多了出现版本冲突的情况很正常(假设模块 A 和 B 都依赖 C,但 C 的版本不同,A 要的是 Cv1,B 要的是 Cv2)。而基于 nodejs 依赖的查找原理(从当前目录开始往上级目录查找 node_modules 下的依赖),将依赖放在本地( B 和 C 各自在其目录下放一个对应版本的 C )可以很好的解决依赖版本冲突问题。
|
23
MoccaCafe 2020-01-05 18:46:03 +08:00
npm 最严重的问题就是安装依赖之后,有上万个细小文件,严重占据 inode 或者说复制困难。像 java 的 jar 包就非常合理了,java 虽然繁琐但是许多设计思想还是很好的
|
24
murmur 2020-01-05 18:46:30 +08:00
所以才有了 npm3,所以才有 yarn,npm 老版本的树状结构再 windows 下简直是噩梦,以前真的就是没法用
|
25
zhbhun 2020-01-05 18:47:03 +08:00
在 window 上的 nodejs 开发体验确实不好,我现在都是在 window 上用 vagrant 开个虚拟机,然后使用 vscode 的 ssh 远程开发,即使依赖再多 node_moduels 也是秒删的,node 运行时 cpu 占用率也低,风扇噪音也小了很多。
|
26
Juszoe 2020-01-05 19:00:26 +08:00
Node 之父也认为 node_modules 的设计是个错误
|
27
sessionreckon 2020-01-05 19:27:35 +08:00
@jybox Java 这种情况是可解的,就是比较麻烦,需要重新用 shade 打个包
|
28
Bromine0x23 2020-01-05 19:45:46 +08:00 1
个人认为 npm 的问题主要还是 node_modules
1) 依赖数量多(普遍上千),依赖安装后产生了大量文件夹和小文件,文件系统负担很大(当然这个可能算 js 依赖本身的性质导致的) 2) 默认将依赖解压到项目下的行为导致了重复存储,结合 1) 加剧了文件系统负担。 不同项目依赖不同版本的问题和使用全局存储是不冲突的,只要在路径上加上版本信息就行 比如 maven 用 package/version/ 或者 gems 用 package-version/ |
29
optional 2020-01-05 19:55:18 +08:00 via iPhone
@lihongjie0209 没有银弹解决所有人的需求和爱好
|
30
wangyzj 2020-01-05 20:20:45 +08:00
说实话
我更喜欢每个项目单独管理自己的依赖 干净 |
31
lihongjie0209 OP @wangyzj #30 难道 maven 做不到?
|
32
lewis89 2020-01-05 20:54:44 +08:00
@jybox #17 maven 是有一个默认优先的 ,最短引用路径的版本优先机制 ,
你没遇到过坑,我遇到过 那种动态反射拿出来了一个老版本的 class 但是用新版本的方法去调用 结果 出现 class 没有这种方法的报错,这种问题在 maven 打包的 java 里面经常遇到 |
33
lewis89 2020-01-05 20:58:03 +08:00
@lihongjie0209 #31
@jybox #17 https://segmentfault.com/a/1190000014938685 看看 maven 吧 一样有很多问题,版本依赖冲突这种问题 没什么好办法 ,只能通过人为管理的办法。 |
34
lewis89 2020-01-05 21:04:08 +08:00
@lihongjie0209 #31 我被 maven 坑了也不是一次两次了,最后还是想办法 excluse 掉一些引用 避免默认的最短路径优先的仲裁机制。
|
35
autoxbc 2020-01-05 21:08:55 +08:00
所以 Deno 里没有包管理,依赖什么就 import 网络地址,Deno 扫描一下全局缓存着
|
36
wangyzj 2020-01-05 21:21:23 +08:00
@lihongjie0209 没用过 maven
可你问的不是全局管理包吗 |
38
uxstone 2020-01-05 22:12:27 +08:00
https://v2ex.com/t/334487
三年前 我也提问过同样的问题, 慢慢习惯下来也就这样了 |
39
lihongjie0209 OP @uxstone #37 我看到这种说法的时候也是挺懵逼的, 这种情况应该尽量避免, 避免不了就特殊处理, 从来没把这种东西当作一个正常的需求
|
40
lihongjie0209 OP @wangyzj #36 我说的是全局存储, 动态加载, 没必要拷贝到当前项目下
|
41
lihongjie0209 OP @lewis89 #34 每次加依赖都应该把 effective pom 导出来看看, 这样可以避免很多问题
|
42
abcbuzhiming 2020-01-05 22:29:33 +08:00
说 npm 已经解决了的各位,我问一个问题,一个包,怎么才能在全局安装多个版本?到目前为止,我就没发现默认的 npm 有多版本共存的解决办法
|
43
lewis89 2020-01-05 22:34:05 +08:00
@lihongjie0209 #41 但是 Java 有些地方是动态加载 调用的 还是没法避免..
|
44
lihongjie0209 OP @lewis89 #43 这种问题应该会在开发和测试阶段就暴露出来, 同样的, 使用 BeanUtils 也只能在运行时报错, 只要使用了反射, 这种问题就无法避免
|
45
MiracleKagari 2020-01-05 22:40:52 +08:00 via Android
@uxstone 不是你用,是你引入的依赖用的依赖是不同的版本
|
46
YuTengjing 2020-01-05 22:45:00 +08:00
因为 node 是基于文件系统的模块化方案,设计上没有所谓的 classpath,必然是所有包都要安装到 node_modules 下。
|
47
jybox 2020-01-05 23:22:35 +08:00
@uxstone
@lihongjie0209 因为你控制不了你依赖的其他包作者用什么版本呀。举个例子 lodash 是 NPM 上被依赖最多的包,你项目依赖的很多其他包可能也依赖了 lodash,而他们依赖的版本可能和你不同。 |
48
Kilerd 2020-01-06 00:51:21 +08:00
有一说一,为什么还有人觉得 java 的包依赖管理好的呢
|
49
ashong 2020-01-06 02:01:48 +08:00 via iPhone
这正是 npm 的优点
|
50
zhw2590582 2020-01-06 08:39:09 +08:00 via iPhone
我当时也有这样的疑问
|
51
yinzhili 2020-01-06 08:52:37 +08:00
nodejs 这种新生事物各种包管理工具还有点混乱,和已经成熟的 java 生态系统没法比
|
52
lihongjie0209 OP @Kilerd #48 有什么缺点吗?
|
53
lihongjie0209 OP @yinzhili #51 我也是这么想的, 以后的项目我们是不是应该直接照抄业界也有的实现, 然后再根据自己的需求做定制化, 这样可以避免走一些别人已经踩过的坑.
|
54
yhxx 2020-01-06 09:31:14 +08:00
有的
PnP、pnpm、tink 都是这种思路 |
55
deleteDB 2020-01-06 09:36:00 +08:00
别问 问就是前端不行
一会是接口展示不行 一会是包管理不行 java 最棒 手动狗头保命 |
56
lihongjie0209 OP @yhxx #54 目前来看 pnp 和 tink 都在开发中, 还没有大规模使用, 而且 pnp 和 tink 都是与 hack nodejs 的 require, 如果与 wepack 使用, pnp 还需要使用自己的 loader, 总体来说改动还是比较大的
|
57
lihongjie0209 OP @deleteDB #55 你这种话没有任何意义, 行不行是一个很主观的东西, 但是有些东西缺点很明显, 还不能拿出来讨论了?
|
58
hitaoguo 2020-01-06 09:56:06 +08:00
1.路径会不会有问题?
你的电脑跟我的电脑全局安装的路径可能会不一样,项目里面如何引用。 |
59
hitaoguo 2020-01-06 09:57:24 +08:00
2.移除包的时候,会不会冗余?
比如这个包我只在一个项目里面用了,那我在项目里面 npm un 的时候会不会把全局的也删掉,那如果多个项目引用怎么判断。 |
60
yannxia 2020-01-06 09:57:57 +08:00
还是 Rust 的 Cargo 的包管理模式好啊,随便你怎么依赖不同的版本。Maven 的问题就是多版本依赖的问题,至于 NPM 的问题,包的质量的太低是真的。
|
61
deleteDB 2020-01-06 09:59:04 +08:00
@lihongjie0209
“有些东西缺点很明显, 还不能拿出来讨论了?” 换做我, 我会问为什么当初这么设计,为什么没考虑到这些缺陷?有什么原因么?如何避免? 为什么调侃你, 你想过么?有的人说话特别武断还不能调侃了? 哈哈哈 |
62
lihongjie0209 OP @hitaoguo #59 这些都是可以配置的, 而且在 maven 中不存在一个 uninstall 的概念, 你的项目如果不需要这个依赖, 那么就直接在 pom 中移除就好了
|
63
lihongjie0209 OP @deleteDB #61 看一下我的问题
|
64
deleteDB 2020-01-06 10:02:18 +08:00 1
@lihongjie0209 看一下你平时留言的表达吧 哈哈 就是想怼你
|
65
deleteDB 2020-01-06 10:02:46 +08:00
@lihongjie0209 其他帖子 别人的帖子里都看看
|
66
szq8014 2020-01-06 10:03:13 +08:00 1
感觉很多人都理解错了, 依赖 [存储] 和 [加载] 是两回事。。
嵌套存储: app -- m1-v1 -- -- m3-v1 -- m2-v1 -- -- m1-v2 -- -- -- m3-v2 扁平存储 app -- m1-v1 -- m1-v2 -- m2-v1 -- m3-v1 -- m3-v2 扁平存储显然是并不存在什么多版本共存的问题,而且比嵌套存储更节省那一点点存储空间的,也并不限制你打包只能加载一个模块的一个版本,有能力当然可以把 commons-lang3 的所有版本全打进 fat-jar 或 war 包里,只不过 java 的默认类加载制度的限制导致 maven 只能默认选一个它约定下的最合适的一个版本,这并不是扁平化的存储导致的。 想想如果 java 需要支持多版本 jar 包存在该如何改呢? 1. 改语法 import, 在包里面加上版本? 需要源码改动不太现实~ 2. 把依赖关系带到运行时告诉类加载器,有一个类似 maven 的 pom.xml 的东西,告诉这个类依赖的版本。比如打包时直接把所有的 jar 重新打,在 META-INF 里面加上依赖描述。 3. 学 nodejs 弄目录嵌套 在 java 里面多版本共存还有许多问题,比如两个版本的同一个类在 jvm 看来是两个完全没关系的东西,假如一个接口返回这个类实例传再传给另一个接口时,虽然参数是需要这个类,传的也是,但是仅仅是因为不是同一个类加载器加载的,会报 class cast exception 的。 这是 java 的问题,不是扁平化包管理的问题。 所以大家别吐槽 maven 了,锅不在 maven~ |
67
lihongjie0209 OP @szq8014 #66 要在 Java 中实现一个包 N 个版本的话要上 OSGI, 目前我还没遇到过这种需求
|
68
whypool 2020-01-06 10:22:56 +08:00
npm 除了质量低,没槽点了
|
69
wangxiaoaer 2020-01-06 10:25:36 +08:00 1
用 java 这么多年了,就没遇到你们说的一个项目需要依赖同一个库的不同版本这种需求。
但是碰到过因为多个第三方库(假设是 A 和 B )的传递依赖导致依赖了同一个库的不同版本(假设是 C ),maven 和 gradle 都提供了排除某个版本的办法。如果 A 和 B 在运行时必须用不同的 C 版本,那就对 A 和 B 的版本进行调整(弄不好某个一低版本的 A 或者 B 可以共用某个版本的 C ),确保能够兼容。如果还是不行,那就 A 和 B 里面换一个。 但即使这样,上述情况也不多见,基本上 spring boot 一把梭,自己手工添加的第三方依赖很少有冲突的情况。 所以 Node 为了所谓的”项目依赖同一个库的不同版本“这种小众需求而采用嵌套式的依赖存储,不惜放弃存储空间和安装效率,不敢苟同。 不过想想一个函数作为一个库这种**行为都能够广泛存在,也就不难理解了。 |
70
lihongjie0209 OP @wangxiaoaer #69 这个需求应该是类库\中间件的需求, 他们需要保证兼容性, 作为开发者真的很少用到
https://www.baeldung.com/osgi ``` Several Java mission-critical and middleware applications have some hard technological requirements. Some have to support hot deploy, so as not to disrupt the running services – and others have to be able to work with different versions of the same package for the sake of supporting external legacy systems. The OSGi platforms represent a viable solution to support this kind of requirements. ``` |
71
wangxiaoaer 2020-01-06 10:51:45 +08:00
@lihongjie0209 #70 我说的是开发过程中的普通依赖,一般通过我说的几步就解决了,还没到需要上 OSGI 的地步。
|
72
lihongjie0209 OP @wangxiaoaer #71 嗯, 所以我觉得这个需求很奇怪
|
74
yhxx 2020-01-06 11:04:12 +08:00
|
75
Curtion 2020-01-06 11:19:29 +08:00
npm 非常复杂,该机制成本很大,就 npm3 光展平包就花了大量功夫, 而且现在也有一些好处,比可以直接拷贝文件夹实现移动项目;还有一个好处可以直接修改包的源码,而不影响其它项目,因为 npm 的包实在太多了,很多包之间会产生 bug,这时候需要手动修复,如果等作者更新可能会很慢,因为出现 bug 的包不一定是你自己项目的依赖,有可能是项目依赖的依赖
|
76
lihongjie0209 OP @Curtion #75
使用依赖管理的一个原因就是为了每个项目只存储依赖描述文件, 而不需要把几万个依赖上传到 git, 同时还能保证每个开发者都使用一样的依赖 如果我们一旦修改由依赖管理软件下载的源码, 那么别的开发者使用 npm install 的时候就无法和你的依赖版本保持一致了, 正确的做法是把需要修改源码的依赖拷贝到你的项目中, 作为你的源码维护, 同时在依赖管理中去掉这个依赖. |
77
Curtion 2020-01-06 11:44:52 +08:00
@lihongjie0209 #76 移动项目确实毕竟鸡肋,没啥用就不谈了。 至于第二个问题,你说的对,但是这确实又是一个解决办法,我实际遇见过,我使用了一个库 A,它依赖于另一个库 B,而 B 在某些情况下会触发 bug,在 B 已经解决 BUG 的情况下,但是 A 的维护者更新落后,A 中的依赖还是比较落后的版本。
此时修改库 B 的源代码,或者修改 A 的依赖版本是最简单的方式,等到 A 更新后只需要重新 install 就行,如果库是全局作用的,势必会影响其它项目。 当然这只是我一厢情愿找的理由,或许实际上或者全局作用更好。不过 npm 有 cache 机制,一定情况下缓解了下载时间过长的问题。 |
78
Curtion 2020-01-06 11:51:34 +08:00
@lihongjie0209 #76 如果把源代码拷贝出来不可行,一是以后有修复就必须手动更新,二是依赖项的层次可能很深入,我也不太了解如果把依赖提升的话,下层的代码还能不能正确找到包
|
79
szq8014 2020-01-06 11:56:20 +08:00
@lihongjie0209 +1 目前依赖冲突就是通过选一个特定版本就解决了,觉得最麻烦的冲突也就是日志库的冲突了,有些库没有用 slf4j,直接依赖了 log4j 或 commons-logging,而项目用的 log4j2/logback,而解决方式就是用现有的 log4j 接口的实现再转回 slf4j 里面。log4j-to-slf4j, log4j-slf4j-impl 能让人迷糊,别的真没这么费脑子
|
80
fox1955 2020-01-06 12:03:24 +08:00
"每次开发时每次 npm install 几分钟"
- clone 下来后 install 一次, 后面开发时不用 install. 源码是源码, 依赖也是源码, 我们把程序运行需要的完整源码放在一起, 在结构上是非常干净整洁的. 至于 "大量小文件问题", 还是那句话, 一个项目只用 install 一次, 对开发零影响. "磁盘空间问题" 不是问题. |
81
lihongjie0209 OP @fox1955 #80 你不加依赖了吗? 修改 webpack 配置加 loader 和插件也要 install 的
|
82
shyling 2020-01-06 12:34:03 +08:00
其实也很简单。。。npm 算是管理源码的,而 maven 这种是管理 jar 的。。。node 和 jvm 的区别,源码可以随便改,jar 没那么随便。js 复制到 node_modules 里就可以开始魔改了。。。(虽然发布时不建议)
java 系列用 build 工具解决这些问题,build 的时候去改包名解决版本冲突,文件冲突。。node 系列放在了编码时。 集中存放 jar 也会让空间一直膨胀,升级了依赖,删了一个项目之后 maven 可不知道旧的 jar 可以删掉了。。 不过其实也没什么好不好的吧。。。又没啥解决不了的问题。 |
84
yannxia 2020-01-06 12:43:19 +08:00
@shyling 没有吧,A 依赖 X 的 0.1 版本和 B,B 依赖 X 的 0.2 版本。是没有干扰的,Maven 不爱处理这样的问题。至于代码要同时依赖 X 的 0.1 和 0.2,这解决不了,Java 靠 Classloader 能解决,Rust 倒是不知道怎么解决,不过这个场景用的倒是不多,上面那个很常见,所以现在 Maven 直接就搞 BOM。
|
85
shyling 2020-01-06 12:54:01 +08:00
@yannxia 对,其实就是把类似 java 的 shade 把相同的 rename 了,a 用的 0.1 的 b 里的 Type 和 c 用的 0.2 的 b 里的 Type 不是一个
|
86
azh7138m 2020-01-06 13:02:33 +08:00
如果是问支不支持,那是支持做链接的,全局保有一份,链接到项目里面。
因为历史原因,构建工具的 symlinks 处理是存在问题的,如果你不介意 break change,也不介意自己处理,直接用 PnP 就行了。 |
87
love 2020-01-06 13:17:39 +08:00
听起来你就是想节省硬盘?那你可以在工作区根目录安装所有你要用的包,工作区里所有的项目都可以直接用了
|
88
biossun 2020-01-06 17:20:32 +08:00
@zhbhun 其实是可以的,只要模块加载器根据 package.json 中的版本声明并结合 package-lock.json 中的版本信息,就可以处理 A 和 B 模块分别依赖 C 模块的不同版本的问题。
|
89
mgoann 2020-01-07 07:16:48 +08:00 via Android
我也挺讨厌 node 的包管理机制的,重使使用了淘宝镜像很多包下载不下来,随便一个空 react 成千上万个小文件在 node modules 下,移动删除都不方便
另外很多兄弟说为了加载不同版本的依赖包,实际情况这种需求我还真没遇到过 |