Bower 和 npm 有什么区别?

bowernpm之间的根本区别是什么?只需要简单明了的东西。我已经看到一些同事在他们的项目中交替使用bowernpm

答案

所有程序包管理器都有很多缺点。您只需要选择可以忍受的内容即可。

历史

npm开始管理 node.js 模块(这就是为什么软件包默认进入node_modules的原因),但是当与Browserifywebpack结合使用时,它也适用于前端。

Bower是专门为前端创建的,并且考虑到这一点已进行了优化。

回购规模

NPM 是多少,比亭子,包括通用的 JavaScript 大得多(如country-data为国家信息或sorts用于分拣功能是在前端或后端可用)。

Bower 的包装数量要少得多。

样式等的处理

凉亭包括样式等。

npm 专注于 JavaScript。样式要么单独下载,要么需要npm-sasssass-npm类的样式。

依赖处理

最大的区别是 npm 确实嵌套了依赖项(但默认情况下是平坦的),而 Bower 需要平坦的依赖项树(这给用户带来了依赖项解决的负担)

嵌套的依赖关系树意味着您的依赖关系可以具有自己的依赖关系,也可以具有自己的依此类推。这允许两个模块要求具有相同依赖项的不同版本,并且仍然可以工作。请注意,从 npm v3 开始,依赖项树默认情况下将是扁平的(节省空间),并且仅在需要的地方嵌套,例如,如果两个依赖项需要它们自己的 Underscore 版本。

一些项目同时使用这两种方法,即它们将 Bower 用于前端软件包,将 npm 用于开发人员工具,例如 Yeoman,Grunt,Gulp,JSHint,CoffeeScript 等。


资源资源

此答案是 Sindre Sorhus 答案的补充。 npm 和 Bower 之间的主要区别在于它们对待递归依赖项的方式。请注意,它们可以在单个项目中一起使用。

关于npm 常见问题解答 :(从 2015 年 9 月 6 日开始,archive.org 链接)

在不嵌套依赖关系的情况下,避免依赖关系冲突要困难得多。这是 npm 工作方式的基础,并且已被证明是一种非常成功的方法。

Bower主页上:

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 需要与依赖项 C 不同的版本。npm 安装了该依赖项的两个版本,因此无论如何都可以工作,但是 Bower 会给您带来冲突,因为它不喜欢重复(因为在网页上加载相同的资源是效率低下且成本高昂,也会带来一些严重的错误)。您将必须手动选择要安装的版本。这可能会导致其中一个依赖项中断,但是无论如何,这都是您需要修复的问题。

因此,常见的用法是使用 Bower 表示要在网页上发布的软件包(例如, 运行时 ,避免重复),而将 npm 用于其他内容,例如测试,构建,优化,检查等(例如开发时间) ,而不必担心重复)。

npm 3 更新:

与 Bower 相比,npm 3 所做的事情仍然有所不同。它将全局安装依赖性,但仅针对遇到的第一个版本。其他版本安装在树中(父模块,然后是 node_modules)。

  • [node_modules]
    • Dep A v1.0
    • 部门 B v1.0
      • Dep A v1.0 (使用根版本)
    • Dep C v1.0
      • dep A v2.0(此版本与根版本不同,因此它将是嵌套安装)

有关更多信息,我建议阅读npm 3文档。

TL; DR:日常使用中最大的区别不是嵌套的依赖关系…… 而是模块和全局变量之间的区别。

我认为以前的海报涵盖了一些基本区别。 (npm 使用嵌套依赖关系确实对管理大型,复杂的应用程序非常有帮助,尽管我认为这不是最重要的区别。)

但是,令我惊讶的是,没有人明确解释 Bower 和 npm 之间最根本的区别之一。如果您阅读上面的答案,将会在 npm 的上下文中看到经常使用的 “模块” 一词。但是它被随意提及,好像它可能只是语法上的区别。

但是, 模块与全局 (或模块与 “脚本”)之间的区别可能是 Bower 与 npm 之间最重要的区别。 将所有内容放入模块的 npm 方法要求您更改为浏览器编写 Javascript 的方式,几乎可以肯定,这样做会更好。

鲍尔方法:全球资源,如<script>标签

从根本上说,Bower 是关于加载普通脚本文件的。无论这些脚本文件包含什么,Bower 都将加载它们。基本上,这意味着 Bower 就像将所有脚本包含在 HTML 的<head>中普通的<script>中一样。

因此,您使用了相同的基本方法,但是却获得了一些不错的自动化便利:

  • 您曾经需要在开发过程中在项目存储库中包含 JS 依赖项,或者通过 CDN 获得它们。现在,您可以跳过存储库中多余的下载内容,然后有人可以快速bower install并立即在本地获得所需的内容。
  • 如果 Bower 依赖项随后在bower.json指定了自己的依赖bower.json ,那么这些也将为您下载。

但是除此之外, Bower 不会改变我们编写 javascript 的方式 。 Bower 加载的文件内部没有任何变化。特别是,这意味着 Bower 加载的脚本中提供的资源(通常但并非总是如此)仍将定义为全局变量 ,可以从浏览器执行上下文中的任何位置使用。

npm 方法:通用 JS 模块,显式依赖注入

Node 领域中的所有代码(以及因此所有通过 npm 加载的代码)都被构造为模块(具体来说,作为CommonJS 模块格式的实现 ,或者现在作为 ES6 模块)。因此,如果使用 NPM(通过 Browserify 或其他执行相同工作的方式)来处理浏览器端的依赖项,则将以与 Node 相同的方式来构造代码。

比我解决 “为什么要使用模块?” 问题的人要聪明,但这是一个总结:

  • 模块内部的任何内容都有效地命名空间 ,这意味着它不再是全局变量,并且您不能无意间无意地引用它。
  • 必须将模块内部的所有内容故意注入特定的上下文(通常是另一个模块)中才能使用它
  • 这意味着您可以在应用程序的各个部分中拥有相同外部依赖项的多个版本(比如说破折号),它们不会发生冲突 / 冲突。 (这种情况经常出乎意料地发生,因为您自己的代码想使用一个版本的依赖关系,但是您的一个外部依赖关系指定了另一个冲突的版本。或者您有两个外部依赖关系,每个都需要一个不同的版本。)
  • 由于所有依赖项都是手动注入到特定模块中的,因此很容易推断它们。您知道一个事实: “在进行此操作时,我唯一需要考虑的代码就是我故意选择在此处注入的代码”
  • 因为即使注入模块的内容都封装在您为其分配的变量之后,并且所有代码都在有限范围内执行,所以意外和冲突变得非常不可能。如果您没有意识到,或者您会这样做,那么您的一个依赖项中的某件事会意外地重新定义全局变量的可能性要小得多。 (它可能发生,但是通常必须使用window.variable之类的方法来做到这window.variable 。仍然会发生的一种意外情况是分配了this.variable ,而没有意识到this实际上是当前的window上下文。)
  • 当您要测试一个单独的模块时,您可以很容易地知道:到底有什么(依赖项)正在影响该模块中运行的代码?而且,由于您要显式注入所有内容,因此可以轻松模拟那些依赖项。

对我来说,将模块用于前端代码可归结为:在更狭窄的上下文中工作,更易于推理和测试,并且对所发生的事情具有更大的确定性。


仅需 30 秒钟即可学习如何使用 CommonJS / Node 模块语法。在给定的 JS 文件中(该文件将是一个模块),首先声明要使用的任何外部依赖项,如下所示:

var React = require('react');

在文件 / 模块内部,您可以执行通常的操作,并创建一些要向外部用户公开的对象或函数,称其为myModule

在文件末尾,您导出要与世界共享的所有内容,如下所示:

module.exports = myModule;

然后,要在浏览器中使用基于 CommonJS 的工作流,您将使用 Browserify 之类的工具来抓取所有这些单独的模块文件,在运行时封装它们的内容,然后根据需要将它们相互注入。

而且,由于 ES6 模块(您可能会使用 Babel 或类似的东西转换为 ES5 模块)获得了广泛的接受,并且可以在浏览器或 Node 4.0 中使用,因此我们也应该对它们进行全面介绍

有关在此平台中使用模块的模式的更多信息。


编辑(2017 年 2 月):如今,Facebook 的Yarn是 npm 的非常重要的潜在替代品 / 补充:建立在 npm 给您的基础上的快速,确定性,脱机软件包管理。值得一看的任何 JS 项目,特别是因为它很容易交换进 / 出。


编辑(2019 年 5 月)“鲍尔终于被弃用 。故事结束。” (h / t:@DanDascalescu,如下,内容简明。)

而且,虽然 Yarn 仍处于活动状态 ,但是一旦采用了 Yarn 的一些关键功能,它的许多动力就会转移回 npm。

2017 年 10 月更新

鲍尔终于被弃用了 。故事结局。

较旧的答案

来自 Spotify JavaScript 开发人员 Mattias Petter Johansson

在几乎所有情况下,更适合在 Bower 上使用 Browserify 和 npm。与 Bower 相比,它只是用于前端应用程序的更好的打包解决方案。在 Spotify,我们使用 npm 打包整个 Web 模块(html,css,js),并且效果很好。

Bower 将自己称为网络包装经理。如果真是这样,那真是太棒了 - 一个使我作为前端开发人员的生活更美好的软件包管理器将很棒。问题在于 Bower 没有为此目的提供专门的工具。它没有提供我不知道 npm 不能提供的工具,尤其是没有任何工具对前端开发人员特别有用。 对于前端开发人员而言,在 npm 上使用 Bower 根本没有任何好处。

我们应该停止使用凉亭并在 npm 左右合并。幸运的是,这是正在发生的事情

模块计数-Bower vs.npm

使用 browserify 或 webpack,将所有模块连接成较大的缩小文件变得非常容易,这对于性能(特别是对于移动设备)而言非常出色。 Bower 并非如此,Bower 需要更多的劳动力才能获得相同的效果。

npm 还使您能够同时使用多个版本的模块。如果您没有做太多的应用程序开发,那么一开始可能会遇到不好的事情,但是一旦您经历了几轮Dependency 地狱之后,您就会意识到拥有一个模块的多个版本的能力是很糟糕的。很棒的功能。请注意,npm 包含一个非常方便的重复数据删除工具 ,如果确实需要 ,它会自动确保仅使用模块的两个版本 - 如果两个模块都可以使用一个模块的相同版本,则它们将使用。但是,如果他们做不到 ,那么您将非常方便。

(请注意, 截至 2016 年 8 月, Webpack汇总功能被认为比 Browserify 更好。)

Bower 维护模块的单一版本,它仅尝试帮助您为您选择正确 / 最佳的模块。

Javascript 依赖管理:NPM vs Bower vs Volo?

NPM 对于节点模块更好,因为存在模块系统并且您在本地工作。 Bower 对浏览器很有用,因为当前只有全局范围,并且您希望对使用的版本非常有选择性。

我的团队从 Bower 搬到了 npm,原因是:

  • 程序化使用很痛苦
  • Bower 的界面不断变化
  • 某些功能(例如 url 速记)已完全损坏
  • 在同一项目中同时使用 Bower 和 npm 会很痛苦
  • 使 bower.json 版本字段与 git 标签保持同步是很痛苦的
  • 源代码控制!= 包管理
  • CommonJS 支持并不简单

有关更多详细信息,请参见“为什么我的团队使用 npm 而不是 bower”

http://ng-learn.org/2013/11/Bower-vs-npm/找到了这个有用的解释

一方面,创建了 npm 来安装在 node.js 环境中使用的模块,或者安装使用 node.js 构建的开发工具,例如 Karma,lint,minifiers 等。 npm 可以在项目中本地安装模块(默认情况下在 node_modules 中),也可以全局安装模块以供多个项目使用。在大型项目中,指定依赖项的方法是创建一个名为 package.json 的文件,其中包含依赖项列表。当您运行 npm install 时,npm 会识别该列表,然后 npm install 会为您下载并安装它们。

另一方面,创建 Bower 来管理您的前端依赖项。像 jQuery,AngularJS,下划线等之类的库。类似于 npm,它有一个文件,您可以在其中指定一个名为 bower.json 的依赖项列表。在这种情况下,可以通过运行 bower install 来安装您的前端依赖项,默认情况下,它们会安装在一个名为 bower_components 的文件夹中。

如您所见,尽管它们执行相似的任务,但是它们针对的是一组非常不同的库。

对于许多使用 node.js 的人来说,bower 的主要好处是可以管理根本不是 JavaScript 的依赖项。如果他们使用可编译为 javascript 的语言,则 npm 可用于管理其某些依赖项。但是,并非所有依赖项都将是 node.js 模块。其中一些可编译为 javascript 的代码可能具有怪异的特定于源语言的修饰,这使得在用户期望源代码时,将它们传递给已编译为 javascript 的一个不明智的选择。

并非 npm 软件包中的所有内容都需要使用面向用户的 javascript,但是对于 npm 库软件包,至少其中一些应该是。