所有包管理器都有许多缺点。你只需挑选你可以忍受的东西。
npm开始管理 node.js 模块(这就是默认情况下包进入node_modules
的原因),但是当它与Browserify或 WebPack 结合使用时,它也适用于前端。
Bower专为前端而设计,并在此基础上进行了优化。
NPM 是多少,比亭子大得多,包括通用的 JavaScript(如country-data
为国家信息或sorts
用于分拣功能是在前端或后端可用)。
鲍尔的包装数量要少得多。
凉亭包括款式等。
npm 专注于 JavaScript。样式可以单独下载,也可以通过npm-sass
或sass-npm
。
最大的区别是 npm 执行嵌套依赖项(但默认情况下是平坦的),而 Bower 需要一个平面依赖树(将依赖项解析的负担放在用户身上) 。
嵌套依赖树意味着您的依赖项可以拥有自己的依赖项,可以拥有自己的依赖项,依此类推。这允许两个模块需要相同描述的不同版本并且仍然有效。请注意,从 npm v3 开始,依赖关系树将默认为 flat(节省空间),并且只在需要的地方嵌套,例如,如果两个依赖关系需要他们自己的 Underscore 版本。
有些项目同时使用 Bower 作为前端软件包,npm 用于开发人员工具,如 Yeoman,Grunt,Gulp,JSHint,CoffeeScript 等。
这个答案是 Sindre Sorhus 答案的补充。 npm 和 Bower 之间的主要区别在于它们处理递归依赖的方式。请注意,它们可以在一个项目中一起使用。
在npm FAQ :( archive.org 链接从 2015 年 9 月 6 日)
如果没有嵌套依赖关系,就很难避免依赖冲突。这是 npm 工作方式的基础,并且已被证明是一种非常成功的方法。
在Bower主页上:
Bower 针对前端进行了优化。 Bower 使用平面依赖树,每个包只需要一个版本,从而将页面加载降至最低。
简而言之,npm 旨在稳定。 Bower 的目标是最小的资源负荷。如果你绘制出依赖结构,你会看到:
故宫:
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 更新:
与 Bower 相比,npm 3 仍然有所不同。它将全局安装依赖项,但仅适用于它遇到的第一个版本。其他版本安装在树中(父模块,然后是 node_modules)。
TL; DR:日常使用中最大的不同之处不是嵌套依赖... 它是模块和全局变量之间的区别。
我认为之前的海报已经涵盖了一些基本的区别。 (npm 使用嵌套依赖项确实非常有助于管理大型复杂应用程序,尽管我认为这不是最重要的区别。)
然而,我很惊讶没有人明确解释过 Bower 和 npm 之间最基本的区别之一。如果您阅读上面的答案,您会在 npm 的上下文中看到经常使用的 “模块” 一词。但随便提到它,好像它甚至只是一个语法差异。
但是这种模块与全局 (或模块与 '脚本')的区别可能是 Bower 和 npm 之间最重要的区别。 将所有内容放入模块中的 npm 方法要求您改变为浏览器编写 Javascript 的方式,几乎肯定会更好。
<script>
标签从根本上说,Bower 就是要加载普通的脚本文件。无论这些脚本文件包含什么,Bower 都会加载它们。这基本上意味着 Bower 就像将所有脚本包含在 HTML 的<head>
中的普通旧<script>
中一样。
所以,你已经习惯了相同的基本方法,但是你获得了一些很好的自动化方便:
bower install
并立即在本地获得所需内容。 bower.json
指定自己的依赖bower.json
,那么也会为您下载这些依赖项。 但除此之外, Bower 并没有改变我们编写 javascript 的方式 。 Bower 加载的文件里面的内容根本不需要改变。特别是,这意味着由 Bower 加载的脚本中提供的资源(通常但不总是)仍然被定义为全局变量 ,可从浏览器执行上下文中的任何位置获得。
Node land 中的所有代码(以及因此通过 npm 加载的所有代码)都被构造为模块(具体地,作为CommonJS 模块格式的实现 ,或者现在,作为 ES6 模块)。因此,如果您使用 NPM 来处理浏览器端依赖项(通过 Browserify 或执行相同工作的其他事项),您将以与 Node 相同的方式构建代码。
聪明的人比我解决 '为什么模块?' 的问题,但这是一个胶囊摘要:
window.variable
这样的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 的纱线是当今 npm 非常重要的潜在替代 / 补充:快速,确定性,离线包管理,建立在 npm 为您提供的基础上。值得一看任何 JS 项目,特别是因为它很容易交换进 / 出。