bower
和之间的根本区别是npm
什么?只想要简单明了的东西。我看到我的一些同事在他们的项目中使用bower
和npm
互换。
Bower 和 npm 有什么区别?
所有的包管理器都有很多缺点。你只需要选择你可以忍受的。
历史
npm开始管理 node.js module(这就是node_modules
默认情况下包进入的原因),但是当与Browserify或webpack结合使用时,它也适用于前端。
Bower专为前端创建,并考虑到这一点进行了优化。
回购规模
npm 比 bower 大得多,包括通用 JavaScript(例如country-data
用于国家信息或sorts
用于前端或后端可用的排序功能)。
Bower 的包数量要少得多。
样式处理等
Bower 包括样式等。
npm 专注于 JavaScript。样式为单独下载或类似的东西需要npm-sass
或sass-npm
。
依赖处理
最大的不同是 npm 做嵌套依赖(但默认是扁平的),而 Bower 需要扁平的依赖树(把依赖解析的负担放在用户身上)。
嵌套依赖树意味着您的依赖项可以拥有自己的依赖项,这些依赖项可以拥有自己的依赖项,依此类推。这允许两个module需要相同依赖项的不同版本并且仍然可以工作。请注意,从 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 用于您要在网页上发布的包(例如运行时,避免重复),并将 npm 用于其他内容,例如测试、构建、优化、检查等(例如开发时间),其中重复问题较少)。
npm 3 的更新:
与 Bower 相比,npm 3 的处理方式仍然不同。它将全局安装依赖项,但仅针对它遇到的第一个版本。其他版本安装在树中(父module,然后是 node_modules)。
- [节点module]
- 深度 A v1.0
- 深度 B v1.0
深度 A v1.0(使用根版本)
- DEP C v1.0
- dep A v2.0(此版本与root版本不同,所以会嵌套安装)
TL;DR:日常使用中最大的区别不是嵌套依赖……而是module和全局变量之间的区别。
我认为之前的海报已经很好地涵盖了一些基本区别。(npm 使用嵌套依赖确实对管理大型复杂应用程序非常有帮助,尽管我认为这不是最重要的区别。)
然而,令我惊讶的是,没有人明确解释 Bower 和 npm 之间最基本的区别之一。如果您阅读上述答案,您会看到 npm 上下文中经常使用的“module”一词。但它被随意提及,好像它甚至可能只是一个语法差异。
但是这种module与全局变量(或module与“脚本”)的区别可能是 Bower 和 npm 之间最重要的区别。将所有内容放入module的 npm 方法要求您更改为浏览器编写 Javascript 的方式,几乎肯定会变得更好。
Bower 方法:全球资源,如<script>
标签
从根本上说,Bower 是关于加载普通的脚本文件。无论这些脚本文件包含什么,Bower 都会加载它们。这基本上意味着 Bower 就像将所有脚本包含在HTML中的普通<script>
's 中一样<head>
。
因此,与您习惯的基本方法相同,但您会获得一些不错的自动化便利:
- 你曾经需要在你的项目 repo 中包含 JS 依赖项(在开发时),或者通过 CDN 获取它们。现在,您可以在 repo 中跳过额外的下载重量,并且有人可以
bower install
在本地快速并立即获得他们需要的东西。 - 如果 Bower 依赖项在其 中指定了自己的依赖项,那么这些依赖
bower.json
项也会为您下载。
但除此之外,Bower 并没有改变我们编写 javascript 的方式。Bower 加载的文件中的内容根本不需要更改。特别是,这意味着 Bower 加载的脚本中提供的资源(通常,但不总是)仍将定义为全局变量,可从浏览器执行上下文的任何位置使用。
npm 方法:通用 JS module,显式依赖注入
Node 中的所有代码(以及所有通过 npm 加载的代码)都被构建为module(具体来说,作为CommonJS module格式的实现,或者现在作为 ES6 module)。因此,如果您使用 NPM 来处理浏览器端的依赖项(通过 Browserify 或其他可以完成相同工作的东西),您将按照 Node 的方式构建代码。
比我更聪明的人已经解决了“为什么是module?”的问题,但这里有一个简单的总结:
- module内的任何内容都是有效的命名空间,这意味着它不再是全局变量,并且您不能无意中意外引用它。
- module内的任何内容都必须有意注入到特定上下文(通常是另一个module)中才能使用它
- 这意味着您可以在应用程序的各个部分拥有相同外部依赖项(比如说 lodash)的多个版本,并且它们不会发生冲突/冲突。(这出乎意料地经常发生,因为您自己的代码想要使用一个版本的依赖项,但是您的一个外部依赖项指定了另一个冲突。或者您有两个外部依赖项,每个都需要不同的版本。)
- 因为所有依赖项都是手动注入到特定module中的,所以很容易推断它们。您知道一个事实:“我在处理此问题时需要考虑的唯一代码是我有意选择在此处注入的代码”。
- 因为即使是注入module的内容也被封装在您分配给它的变量后面,并且所有代码都在有限的范围内执行,所以意外和冲突变得非常不可能。来自您的依赖项之一的某些内容在您没有意识到的情况下意外地重新定义全局变量的可能性要小得多,或者您会这样做。(它可能发生,但你通常必须用你的方式去做,比如
window.variable
。仍然容易发生的一个事故是赋值this.variable
,没有意识到这this
实际上是window
在当前上下文中。) - 当您想测试单个module时,您可以很容易地知道:究竟还有什么(依赖项)影响了在module内运行的代码?而且,因为您明确地注入了所有内容,所以您可以轻松地模拟这些依赖项。
对我来说,前端代码module的使用归结为:在更窄的上下文中工作,更容易推理和测试,并且对正在发生的事情有更大的确定性。
学习如何使用 CommonJS/Node module语法只需要大约 30 秒。在一个给定的 JS 文件中,它将成为一个module,你首先声明你想要使用的任何外部依赖项,如下所示:
var React = require('react');
在文件/module内部,您可以做任何您通常会做的事情,并创建一些您希望向外部用户公开的对象或函数,可能将其称为myModule
.
在文件的末尾,您可以导出您想与世界分享的任何内容,如下所示:
module.exports = myModule;
然后,要在浏览器中使用基于 CommonJS 的工作流,您将使用 Browserify 之类的工具来获取所有这些单独的module文件,在运行时封装它们的内容,并根据需要将它们相互注入。
而且,由于 ES6 module(您可能会使用 Babel 或类似工具将其转换为 ES5)正在获得广泛接受,并且可以在浏览器或 Node 4.0 中运行,因此我们也应该对这些module进行很好的概述。
更多关于使用本套牌中的module的模式。
编辑(2017 年 2 月):如今,Facebook 的Yarn是 npm 非常重要的潜在替代品/补充品:快速、确定性、离线的包管理,建立在 npm 为您提供的基础上。任何 JS 项目都值得一看,特别是因为它很容易将其换入/换出。
编辑(2019 年 5 月)“鲍尔终于被弃用了。故事结束。” (h/t:@DanDascalescu,下面是简洁的总结。)
而且,虽然 Yarn仍然活跃,但一旦它采用了 Yarn 的一些关键特性,它的很多动力就会转移回 npm。
2017 年 10 月更新
Bower 终于被弃用了。故事结局。
较旧的答案
来自 Spotify 的 JavaScript 开发人员 Mattias Petter Johansson:
在几乎所有情况下,使用 Browserify 和 npm 比 Bower 更合适。它只是比 Bower 更好的前端应用程序打包解决方案。在 Spotify,我们使用 npm 来打包整个 Web module(html、css、js),并且效果很好。
Bower 将自己打造成网络包管理器。如果这是真的,那就太棒了 - 一个让我的前端开发人员生活更美好的包管理器会很棒。问题是 Bower 没有为此提供专门的工具。它没有提供我知道 npm 没有的任何工具,尤其是没有提供对前端开发人员特别有用的工具。对于前端开发人员来说,使用 Bower 而不是 npm 没有任何好处。
我们应该停止使用 bower 并围绕 npm 进行整合。值得庆幸的是,这就是正在发生的事情:
使用 browserify 或 webpack,将所有module连接成大的缩小文件变得非常容易,这对性能来说非常棒,尤其是对于移动设备。Bower 并非如此,这将需要更多的劳动力才能获得相同的效果。
npm 还使您能够同时使用多个版本的module。如果你没有做过很多应用程序开发,这最初可能会让你觉得这是一件坏事,但是一旦你经历了几次依赖地狱,你就会意识到拥有一个module的多个版本的能力是一件非常糟糕的事情很棒的功能。需要注意的是NPM包括一个非常方便的重复数据删除工具,可自动确保您只使用一个module的两个版本,如果你确实有到-如果两个module都可以使用一个module的版本相同,他们会的。但如果他们不能,你有一个非常方便的。
Bower 维护单一版本的module,它只会尝试帮助您为您选择正确/最佳的module。
NPM 更适合节点module,因为有一个module系统并且您在本地工作。Bower 对浏览器很有用,因为目前只有全局范围,并且您希望对使用的版本非常有选择性。