这意味着您可以拥有相同外部依赖项的多个版本(lodash,让's say) in various parts of your application, and they won'碰撞/冲突 . )这种情况经常发生,因为您自己的代码想要使用一个版本的依赖项,但您的一个外部依赖项指定另一个版本那些冲突 . 或者你有两个外部依赖,每个都想要一个不同的版本 . )
因为所有依赖项都是手动注入特定模块的,所以很容易对它们进行推理 . 你知道一个事实:"The only code I need to consider when working on this is what I have intentionally chosen to inject here" .
project root
[node_modules] // default directory for dependencies
-> dependency A
-> dependency B
[node_modules]
-> dependency A
-> dependency C
[node_modules]
-> dependency B
[node_modules]
-> dependency A
-> dependency D
如您所见,它以递归方式安装一些依赖项 . 依赖关系A有三个已安装的实例!
鲍尔:
project root
[bower_components] // default directory for dependencies
-> dependency A
-> dependency B // needs A
-> dependency C // needs B and D
-> dependency D
9 回答
TL;DR: The biggest difference in everyday use isn't nested dependencies... it's the difference between modules and globals.
我认为之前的海报已经涵盖了一些基本的区别 . (npm使用嵌套依赖项确实非常有助于管理大型复杂应用程序,尽管我认为这不是最重要的区别 . )
然而,我很惊讶没有人明确解释过Bower和npm之间最根本的区别 . 如果您阅读上面的答案,您会在npm的上下文中看到经常使用的“模块”一词 . 但随便提到它,好像它甚至只是一个语法差异 .
但是 modules vs. globals (或模块与'scripts')的区别可能是Bower和npm之间最重要的区别 . 将所有内容放入模块中的npm方法要求您改变为浏览器编写Javascript的方式,几乎肯定会更好 .
凉亭方法:全球资源,与<script>标签一样
从根本上说,Bower就是要加载普通的脚本文件 . 无论这些脚本文件包含什么,Bower都会加载它们 . 这基本上意味着Bower就像在HTML的
<head>
中包含所有脚本一样 .所以,你已经习惯了相同的基本方法,但是你获得了一些很好的自动化方便:
您曾经需要在项目仓库中包含JS依赖项(在开发时),或者通过CDN获取它们 . 现在,您可以在回购中跳过额外的下载权重,并且有人可以快速
bower install
并立即在本地获得他们需要的内容 .如果Bower依赖项在其
bower.json
中指定了自己的依赖项,那么也会为您下载这些依赖项 .但除此之外,Bower并没有改变我们编写javascript的方式 . Bower加载的文件里面的内容根本不需要改变 . 特别是,这意味着由Bower加载的脚本中提供的资源(通常但不总是)仍然被定义为全局变量,可从浏览器执行上下文中的任何位置获得 .
npm方法:通用JS模块,显式依赖注入
Node land中的所有代码(以及因此通过npm加载的所有代码)都被构造为模块(具体地,作为CommonJS module format的实现,或者现在,作为ES6模块) . 因此,如果您使用NPM来处理浏览器端依赖项(通过Browserify或执行相同工作的其他事项),您将以与Node相同的方式构建代码 .
聪明的人比我解决'为什么模块?'的问题,但这是一个胶囊摘要:
模块内部的任何内容都是有效的命名空间,这意味着它无意中引用它 .
模块内的任何内容都必须有意地注入特定的上下文(通常是另一个模块)才能使用它
这意味着您可以拥有相同外部依赖项的多个版本(lodash,让's say) in various parts of your application, and they won'碰撞/冲突 . )这种情况经常发生,因为您自己的代码想要使用一个版本的依赖项,但您的一个外部依赖项指定另一个版本那些冲突 . 或者你有两个外部依赖,每个都想要一个不同的版本 . )
因为所有依赖项都是手动注入特定模块的,所以很容易对它们进行推理 . 你知道一个事实:"The only code I need to consider when working on this is what I have intentionally chosen to inject here" .
因为即使注入模块的内容被封装在您分配给它的变量后面,并且所有代码都在有限的范围内执行,所以意外和冲突变得非常不可能 . 很可能,你的某个依赖项中的某些内容会在没有意识到的情况下意外地重新定义全局变量,或者你会这样做 . (它可能会发生,但你通常不得不用你的方式去做,比如
window.variable
. 仍然会发生的一个事故就是分配this.variable
,而不是意识到this
在当前环境中实际上是window
. )当你想测试一个单独的模块时,你会明确地注入所有内容,你可以轻松地模拟这些依赖项 .
对我来说,前端代码模块的使用归结为:在更窄的环境中工作,更容易推理和测试,并且对正在发生的事情有更大的确定性 .
学习如何使用CommonJS / Node模块语法只需要大约30秒 . 在给定的JS文件中,您将首先声明要使用的任何外部依赖项,如下所示:
var React = require('react');
在文件/模块中,你可以做任何你想做的事情,并创建一些你想要向外部用户公开的对象或函数,也许是
myModule
.在文件末尾,您可以导出要与世界共享的任何内容,如下所示:
module.exports = myModule;
然后,使用一个在浏览器中基于CommonJS的工作流程,您将使用Browserify之类的工具来获取所有这些单独的模块文件,在运行时封装它们的内容,并根据需要将它们互相注入 .
并且,由于ES6模块(您可能会通过Babel或类似程序转发到ES5)正在获得广泛认可,并且在浏览器或Node 4.0中都可以使用,我们也应该提及这些模块中的_121155 .
有关在this deck中使用模块的模式的更多信息 .
编辑(2017年2月):Facebook的Yarn是这些天npm非常重要的潜在替代/补充:快速,确定性,离线包管理, Build 在npm为您提供的基础上 . 它's worth a look for any JS project, particularly since it'很容易交换进/出 .
我的团队离开Bower并迁移到npm因为:
程序化使用很痛苦
Bower的界面不断变化
某些功能,如网址速记,完全被破坏了
在同一个项目中同时使用Bower和npm是很痛苦的
保持bower.json版本字段与git标记同步是痛苦的
源代码管理!=包管理
CommonJS支持并不简单
有关更多详细信息,请参阅"Why my team uses npm instead of bower" .
这个答案是Sindre Sorhus答案的补充 . npm和Bower之间的主要区别在于它们处理递归依赖的方式 . 请注意,它们可以在一个项目中一起使用 .
On the npm FAQ: (archive.org链接自2015年9月6日)
On Bower homepage:
简而言之,npm旨在稳定 . Bower的目标是最小的资源负荷 . 如果你绘制出依赖结构,你会看到:
故宫:
如您所见,它以递归方式安装一些依赖项 . 依赖关系A有三个已安装的实例!
鲍尔:
在这里,您可以看到所有唯一依赖项都在同一级别上 .
So, why bother using npm?
也许依赖关系B需要不同版本的依赖关系A而不是依赖关系C.npm安装这个依赖关系的两个版本,所以无论如何它都会工作,但是Bower会给你一个冲突,因为它不喜欢重复(因为在网页上加载相同的资源是非常低效和昂贵,也可能会产生一些严重的错误) . 您必须手动选择要安装的版本 . 这可能会导致其中一个依赖项中断,但这是您需要修复的东西 .
因此,对于要在网页上发布的软件包(例如运行时,避免重复),通常使用Bower,并使用npm进行其他内容,如测试,构建,优化,检查等(例如开发时间) ,重复不太重要) .
Update for npm 3:
与Bower相比,npm 3仍然有所不同 . 它将全局安装依赖项,但仅适用于它遇到的第一个版本 . 其他版本安装在树中(父模块,然后是node_modules) .
[node_modules]
dep A v1.0
dep B v1.0
dep A v1.0(使用root版本)
dep C v1.0
dep A v2.0(此版本与根版本不同,因此它将是嵌套安装)
有关更多信息,我建议阅读docs of npm 3
对于使用node.js的许多人来说,bower的一个主要好处是管理非javascript的依赖项 . 如果他们使用的语言编译为javascript,则可以使用npm来管理他们的一些依赖项 . 但是,并非所有的依赖项都是node.js模块 . 编译为javascript的一些可能具有奇怪的源语言特定的修改,使得当用户期望源代码时,将它们传递到javascript是一个不优雅的选项 .
并非npm包中的所有内容都需要面向用户的javascript,但对于npm库包,至少其中一些应该是 .
所有包管理器都有许多缺点 . 你只需挑选你可以忍受的东西 .
历史
npm开始管理node.js模块(这就是默认情况下包进入
node_modules
的原因),但是当与Browserify或WebPack结合使用时,它也适用于前端 .Bower仅为前端创建,并在考虑到这一点时进行了优化 .
回购的大小
npm远比bower大得多,包括通用JavaScript(如
country-data
用于国家/地区信息,或sorts
用于排序可在前端或后端使用的功能) .鲍尔的包装数量要少得多 .
款式处理等
凉亭包括款式等 .
npm专注于JavaScript . 样式可以单独下载,也可以通过
npm-sass
或sass-npm
等方式下载 .依赖处理
最大的区别是npm执行嵌套依赖项(但默认情况下是平坦的),而Bower需要一个平面依赖树(将依赖项解析的负担放在用户身上) .
嵌套依赖树意味着您的依赖项可以拥有自己的依赖项,可以拥有自己的依赖项,依此类推 . 这允许两个模块需要相同描述的不同版本并且仍然有效 . 请注意,从npm v3开始,依赖关系树将默认为flat(节省空间),并且只在需要的地方嵌套,例如,如果两个依赖关系需要他们自己的Underscore版本 .
有些项目同时使用Bower作为前端软件包,npm用于开发人员工具,如Yeoman,Grunt,Gulp,JSHint,CoffeeScript等 .
资源
从http://ng-learn.org/2013/11/Bower-vs-npm/找到这个有用的解释
Bower和Npm是javascript的包管理器 .
Bower
Bower专为前端开发而设计 . 它使用平面依赖树,每个包只需要一个版本,减少了页面加载 . 它主要旨在最小化资源负荷 . 它的贡献者较少,因此开发过程缓慢 .
Bower有一个名为bower.json的配置文件 . 在这个文件中,我们可以维护Bower的配置,比如我们需要哪些依赖项以及许可证详细信息,描述,名称等 . Bower适用于jquery,angular,react,ember,knockout,backbone等前端软件包 .
Npm
Npm最常用于管理Node.js模块,但它也适用于前端 . 它使用嵌套依赖树以及平面依赖树 . 它很受欢迎,并且有很多贡献者 . 所以它的新版本总是带来令人兴奋的功能 .
Npm有一个名为package.json的配置文件 . 在这个文件中,我们可以维护Npm的配置,比如我们需要哪些依赖项以及许可证详细信息,描述,名称等 . Npm提供Dependencies和DevDependencies . 依赖关系将下载和维护前端文件,如Jquery,Angular等 . DevDependencies将下载和维护Grunt,Gulp,JSHint等开发工具 .
这显然在前端工作得不好,因为我们的项目中需要jQuery . 我们只需要jQuery的一个副本,但是当另一个包需要jQuery时,它将再次下载jQuery的一个副本 . 这是Npm的主要缺点之一 .
关键注意:许多项目使用两者的原因是他们使用Bower作为前端软件包,Npm使用开发人员工具 . 在项目中使用包管理器会使您的工作流程变得更加困难 . Npm 3加上browserify或webpack是现在的方法 .
2017 -Oct update
鲍尔终于deprecated . 故事结局 .
旧答案
From Mattias Petter Johansson, JavaScript developer at Spotify:
(请注意,截至2016年8月,Webpack和rollup被广泛认为比Browserify更好 . )
Bower维护单个版本的模块,它只会帮助您为您选择正确/最佳的模块 .
NPM对于节点模块更好,因为有一个模块系统,你在本地工作 . Bower适用于浏览器,因为目前只有全局范围,并且您希望对所使用的版本保持高度选择性 .