所有的包管理器都有很多缺点。你只需要选择你能接受的。
npm开始管理node.js模块(这就是为什么包默认进入node_modules
的原因),但是当与Browserify或WebPack结合时,它也适用于前端。
Bower完全是为前端创建的,并在此基础上进行了优化。
npm比bower大得多,包括通用JavaScript(如country-data
用于国家信息,或者sorts
用于排序功能,可在前端或后端使用)。
鲍尔的包裹数量要少得多。
鲍尔包括风格等。
npm主要关注JavaScript。样式要么是单独下载的,要么是npm-sass
或sass-npm
之类的东西需要的。
最大的区别在于,npm做嵌套的依赖(但默认情况下是平面的),而Bower需要平面的依赖树(将依赖解析的负担放在用户身上)。
嵌套依赖树意味着依赖项可以有自己的依赖项,而依赖项也可以有自己的依赖项,以此类推。这允许两个模块需要相同依赖项的不同版本,并且仍然可以工作。注意,自NPMV3以来,依赖项树默认情况下是扁平的(节省空间),只在需要的地方嵌套,例如,如果两个依赖项需要它们自己版本的下划线。
有些项目同时使用这两种工具,他们将Bower用于前端包,将npm用于开发工具,如Yeoman,Grunt,Gulp,JSHint,CoffeeScript等。
这个答案是对Sindre Sorhus答案的补充。npm和Bower之间的主要区别在于它们处理递归依赖关系的方式。注意,它们可以在单个项目中一起使用。
关于国家预防机制的常见问题:(Archive.org链接,从6 Sep 2015)
在没有嵌套依赖关系的情况下,避免依赖关系冲突要困难得多。这是国家预防机制工作方式的基础,并已被证明是一种极为成功的方法。
在鲍尔主页上:
Bower针对前端进行了优化。Bower使用平面依赖树,每个包只需要一个版本,将页面负载降至最低。
简而言之,npm的目标是稳定。Bower的目标是最小的资源负载。如果你画出依赖关系结构,你会看到这个:
NPM:
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
在这里,您可以看到所有唯一的依赖关系都在同一级别上。
那么,为什么要费心使用NPM呢?
可能依赖项B需要依赖项a的不同版本,而不是依赖项C.npm会安装这两个版本的依赖项,所以无论如何它都能正常工作,但是Bower会给你一个冲突,因为它不喜欢重复(因为在网页上加载相同的资源效率很低,成本很高,而且还会出现一些严重的错误)。您必须手动选择要安装的版本。这可能会导致其中一个依赖关系中断,但无论如何,这是您需要修复的。
因此,通常的用法是Bower用于您想要发布在您的网页上的包(例如,运行时,在这里您可以避免重复),而npm用于其他东西,如测试,构建,优化,检查等(例如,开发时间,在这里重复不太受关注)。
npm 3的更新:
与鲍尔相比,NPM3仍然做着不同的事情。它将在全局范围内安装依赖项,但仅针对它遇到的第一个版本。其他版本安装在树中(父模块,然后是node_modules)。
有关更多信息,我建议阅读NPM3的文档
TL;DR:日常使用中最大的区别不是嵌套依赖。。。这是模块和全局的区别。
我认为以前的海报已经很好地涵盖了一些基本的区别。(NPM对嵌套依赖项的使用在管理大型复杂应用程序方面确实非常有帮助,尽管我认为这不是最重要的区别。)
然而,我感到惊讶的是,没有人明确解释鲍尔和NPM之间最基本的区别之一。如果您阅读了上面的答案,您将会看到“模块”这个词经常在NPM的上下文中使用。但它被随便提到,好像它甚至可能只是句法上的差异。
但是模块与全局(或者模块与“脚本”)的区别可能是Bower和NPM之间最重要的区别。将所有内容放入模块中的npm方法要求您改变为浏览器编写Javascript的方式,几乎可以肯定是为了更好。
在根目录下,Bower是关于加载普通旧脚本文件的。无论这些脚本文件包含什么,Bower都会加载它们。这基本上意味着Bower就像是在HTML的中包含了所有纯旧的
中的脚本。
因此,与您习惯的基本方法相同,但您可以获得一些很好的自动化便利性:
bower install
,并立即在本地获得他们需要的内容。Bower.json
中指定了它自己的依赖项,那么这些依赖项也将为您下载。但除此之外,Bower并没有改变我们编写JavaScript的方式。鲍尔加载的文件中没有任何内容需要更改。特别是,这意味着在Bower加载的脚本中提供的资源将(通常,但不总是)仍然被定义为全局变量,可以从浏览器执行上下文中的任何地方获得。
Node land中的所有代码(因此所有通过npm加载的代码)都被结构化为模块(具体地说,作为CommonJS模块格式的实现,或者现在作为ES6模块)。因此,如果您使用NPM处理浏览器端依赖关系(通过Browserify或其他做相同工作的东西),您将以与Node相同的方式构造代码。
比我更聪明的人已经解决了“为什么要使用模块?”的问题,但下面是一个胶囊摘要:
window.variable
的方法来实现。仍然容易发生的一个事故是分配this.variable
,而没有意识到此
实际上是当前上下文中的window
。)对我来说,在前端代码中使用模块可以归结为:在更窄的上下文中工作,这样更容易推理和测试,并且对正在发生的事情有更大的确定性。
学习如何使用commonjs/node模块语法只需要大约30秒。在一个给定的JS文件(它将是一个模块)中,首先声明要使用的任何外部依赖项,如下所示:
var React=require('React');
在文件/模块内部,您可以做任何您通常会做的事情,并创建一些您想要向外部用户公开的对象或函数,可能将其称为MyModule
。
在一个文件的末尾,您导出您想要与世界共享的任何内容,如下所示:
module.exports=MyModule;
然后,为了在浏览器中使用基于CommonJS的工作流,您将使用Browserify之类的工具来获取所有这些单独的模块文件,在运行时封装它们的内容,并根据需要将它们相互注入。
而且,由于ES6模块(您可能会通过Babel或类似的方式传输到ES5)正在获得广泛的接受,并且可以在浏览器或Node4.0中工作,因此我们也应该对这些模块进行一个很好的概述。
更多关于在此平台中使用模块的模式。
编辑(2017年2月):Facebook的Yarn是当前npm的一个非常重要的潜在替代/补充:快速,确定,离线的包管理,建立在npm提供的基础上。对于任何JS项目来说,它都值得一看,特别是因为它可以很容易地调入/调出它。
编辑(2019年5月)“鲍尔终于被否决了。故事结束。”(以下H/T:@Dandascalescu,为精炼总结。)
而且,虽然纱线仍然活跃,但当它采用了纱线的一些关键特性后,它的许多动力又转回了npm。