bower
和之间的根本区别是什么npm
?只想要简单明了的东西。我见过我的一些同事在他们的项目中使用bower
和npm
互换。
8 回答
所有包管理器都有很多缺点。你只需要选择你可以忍受的。
历史
npm开始管理 node.js 模块(这就是默认情况下包进入的原因),但是当与Browserify或webpacknode_modules
结合使用时,它也适用于前端。
Bower是专门为前端创建的,并为此进行了优化。
回购规模
npm 比 bower 大得多,包括通用 JavaScript(例如country-data
用于国家信息或sorts
可用于前端或后端的排序功能)。
Bower 的包裹数量要少得多。
样式处理等
Bower 包括样式等。
npm 专注于 JavaScript。样式可以单独下载,也可以通过类似npm-sass
或sass-npm
.
依赖处理
最大的不同是 npm 做了嵌套依赖(但默认是扁平的),而 Bower 需要扁平的依赖树(将依赖解析的负担放在用户身上)。
嵌套的依赖树意味着你的依赖可以有自己的依赖,也可以有自己的依赖,依此类推。这允许两个模块需要相同依赖项的不同版本并且仍然可以工作。注意从 npm v3 开始,依赖树默认是扁平的(节省空间)并且只在需要的地方嵌套,例如,如果两个依赖项需要它们自己的 Underscore 版本。
一些项目同时使用两者:他们使用 Bower 作为前端包,使用 npm 作为开发工具,如 Yeoman、Grunt、Gulp、JSHint、CoffeeScript 等。
资源
- 嵌套依赖关系- 深入了解为什么 node_modules 以它的方式工作
这个答案是对 Sindre Sorhus 答案的补充。npm 和 Bower 之间的主要区别在于它们处理递归依赖项的方式。请注意,它们可以在单个项目中一起使用。
在npm 常见问题解答上:(archive.org 链接自 2015 年 9 月 6 日)
如果不嵌套依赖项,要避免依赖项冲突要困难得多。这是 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 不同版本的依赖项 A。npm 安装了此依赖项的两个版本,因此无论如何它都可以工作,但 Bower 会给你一个冲突,因为它不喜欢重复(因为在网页上加载相同的资源是非常低效且成本高昂,也可能会产生一些严重的错误)。您必须手动选择要安装的版本。这可能会导致其中一个依赖项会中断,但无论如何您都需要修复它。
因此,常见的用法是 Bower 用于您要在网页上发布的包(例如runtime,避免重复),并将 npm 用于其他东西,例如测试、构建、优化、检查等(例如开发时间,其中重复不太重要)。
npm 3 的更新:
与 Bower 相比,npm 3 的处理方式仍然不同。它将全局安装依赖项,但仅限于它遇到的第一个版本。其他版本安装在树中(父模块,然后是 node_modules)。
- [节点模块]
- 德普A v1.0
- DEP B v1.0
德普A v1.0(使用根版本)
- 开发C v1.0
- dep A v2.0(这个版本和root版本不同,所以会嵌套安装)
有关更多信息,我建议阅读npm 3 的文档
TL;DR:日常使用中最大的区别不是嵌套依赖项……而是模块和全局变量之间的区别。
我认为之前的海报已经很好地涵盖了一些基本区别。(npm 对嵌套依赖项的使用在管理大型、复杂的应用程序方面确实非常有帮助,尽管我认为这不是最重要的区别。)
然而,令我惊讶的是,没有人明确解释 Bower 和 npm 之间最基本的区别之一。如果您阅读了上面的答案,您会看到在 npm 上下文中经常使用的“模块”一词。但它被随意提及,好像它甚至可能只是语法差异。
但是模块与全局变量(或模块与“脚本”)的这种区别可能是 Bower 和 npm 之间最重要的区别。将所有内容都放在模块中的 npm 方法要求您更改为浏览器编写 Javascript 的方式,几乎可以肯定会变得更好。
Bower 方法:全球资源,点赞<script>
标签
在根目录下,Bower 是关于加载普通的旧脚本文件。无论这些脚本文件包含什么,Bower 都会加载它们。这基本上意味着 Bower 就像在HTML<script>
中将所有脚本包含在 plain-old 中一样。<head>
所以,你习惯了同样的基本方法,但你会得到一些很好的自动化便利:
- 您曾经需要在项目 repo 中包含 JS 依赖项(在开发时),或者通过 CDN 获取它们。现在,您可以在 repo 中跳过额外的下载权重,并且有人可以在
bower install
本地快速并立即获得他们需要的东西。 - 如果 Bower 依赖项在其 中指定了它自己的依赖
bower.json
项,那么也会为您下载这些依赖项。
但除此之外,Bower 并没有改变我们编写 javascript的方式。Bower 加载的文件中的内容根本不需要更改。特别是,这意味着 Bower 加载的脚本中提供的资源将(通常但不总是)仍被定义为全局变量,可在浏览器执行上下文的任何位置使用。
npm 方法:通用 JS 模块,显式依赖注入
Node 领域的所有代码(以及因此通过 npm 加载的所有代码)都被构造为模块(具体来说,作为CommonJS 模块格式的实现,或者现在,作为 ES6 模块)。因此,如果您使用 NPM 处理浏览器端依赖项(通过 Browserify 或其他完成相同工作的工具),您将像 Node 一样构建代码。
比我更聪明的人已经解决了“为什么是模块?”的问题,但这里有一个摘要:
- 模块内的任何内容都是有效的namespaced,这意味着它不再是全局变量,您不会无意中意外引用它。
- 模块内的任何东西都必须有意注入到特定的上下文(通常是另一个模块)中才能使用它
- 这意味着您可以在应用程序的各个部分中拥有相同外部依赖项的多个版本(例如,lodash),并且它们不会发生冲突/冲突。(这种情况经常发生,因为您自己的代码想要使用一个版本的依赖项,但是您的一个外部依赖项指定了另一个发生冲突的外部依赖项。或者您有两个外部依赖项,每个依赖项都需要不同的版本。)
- 因为所有依赖项都是手动注入到特定模块中的,所以很容易推断它们。你知道一个事实:“我在处理这个问题时需要考虑的唯一代码是我有意选择在这里注入的代码”。
- 因为即使是注入模块的内容也封装在你分配给它的变量后面,并且所有代码都在有限的范围内执行,所以意外和冲突变得非常不可能。您的某个依赖项中的某些内容在您没有意识到的情况下意外重新定义全局变量的可能性要小得多,或者您会这样做。(它可能会发生,但您通常必须不遗余力地做到这一点,例如
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 中工作,因此我们也应该对这些模块进行很好的概述。
更多关于在本套牌中使用模块的模式。
编辑(2017 年 2 月):Facebook 的Yarn是当今 npm 非常重要的潜在替代品/补充品:基于 npm 提供的快速、确定性、离线包管理。任何 JS 项目都值得一看,特别是因为它很容易换入/换出。
编辑(2019 年 5 月)“Bower 终于被弃用了。故事结束。” (h/t:@DanDascalescu,下面,简要总结。)
而且,虽然 Yarn仍然活跃,但一旦它采用了 Yarn 的一些关键特性,它的很多动力就转移回了 npm。
2017 年 10 月更新
Bower 终于被弃用了。故事结局。
较旧的答案
来自 Spotify 的 JavaScript 开发人员 Mattias Petter Johansson:
在几乎所有情况下,使用 Browserify 和 npm 而不是 Bower 更合适。它只是一个比 Bower 更好的前端应用程序打包解决方案。在 Spotify,我们使用 npm 来打包整个 web 模块(html、css、js)并且效果很好。
Bower 将自己标榜为网络的包管理器。如果这是真的,那就太棒了——作为前端开发人员让我的生活变得更好的包管理器会很棒。问题是 Bower 没有为此提供专门的工具。它没有提供我所知道的 npm 没有的工具,尤其是没有对前端开发人员特别有用的工具。前端开发人员使用 Bower 而不是 npm 根本没有任何好处。
我们应该停止使用 bower 并围绕 npm 进行整合。值得庆幸的是,这就是正在发生的事情:
使用 browserify 或 webpack,将所有模块连接成大型压缩文件变得非常容易,这对性能来说非常棒,尤其是对于移动设备。Bower 并非如此,它需要更多的劳动力才能获得相同的效果。
npm 还为您提供同时使用多个版本的模块的能力。如果你没有做过太多的应用程序开发,这最初可能会让你觉得这是一件坏事,但是一旦你经历了几次依赖地狱,你就会意识到拥有一个模块的多个版本的能力是相当糟糕的很棒的功能。请注意,npm 包含一个非常方便的重复数据删除工具,它会自动确保您只使用一个模块的两个版本(如果您确实需要) - 如果两个模块都可以使用一个模块的相同版本,它们会。但如果他们不能,你有一个非常方便的。
Bower 维护单一版本的模块,它只试图帮助您选择正确/最佳的模块。
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 会识别该列表,然后会为您下载并安装它们。
另一方面,创建 bower 是为了管理您的前端依赖项。jQuery、AngularJS、underscore 等库。与 npm 类似,它有一个文件,您可以在其中指定名为 bower.json 的依赖项列表。在这种情况下,您的前端依赖项是通过运行 bower install 安装的,默认情况下会将它们安装在名为 bower_components 的文件夹中。
如您所见,尽管它们执行类似的任务,但它们针对的是一组非常不同的库。
对于许多使用 node.js 的人来说,bower 的一个主要好处是用于管理根本不是 javascript 的依赖项。如果他们使用编译为 javascript 的语言,npm 可以用来管理他们的一些依赖项。然而,并不是所有的依赖都是 node.js 模块。一些编译为 javascript 的代码可能有奇怪的源语言特定的修改,当用户期望源代码时,将它们传递给编译为 javascript 的选项是一个不雅的选择。
并非 npm 包中的所有内容都需要面向用户的 javascript,但对于 npm 库包,至少其中一些应该是。