在NPM package.json文件中,dependencies、devDependencies和peerDependencies有什么区别?

2859

这份文档回答了我的问题,但解释得不够清楚。有没有人能用更简单的话来说一下?如果难以选择简单的词语,可以举例说明吗?

另外,我还添加了peerDependencies,这个也与之密切相关,可能会引起混淆。


78
现在还有“optionalDependencies”(可选依赖项)optionalDependencies - Aidan Feldman
310
"optionalDependencies" 是我今天学到的矛盾词。 - Nick Bull
6
npm文档表示: "dependencies": 应用程序在生产环境中需要的包。 "devDependencies": 仅在本地开发和测试中需要的包。请查看链接:https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file - Deke
依赖项是您的库使用的软件包引用,如果没有这些引用,它将无法工作,并且必须随您的库安装一起自动安装。在查看peerDependencies时,npm会发出警告消息,以防找不到指定的软件包。它不会为您安装任何软件包。有关详细说明,请参阅链接:https://medium.com/p/16f43d6c7e45 - Goofy
17个回答

3194
重要行为差异摘要:
  • dependencies需要安装在以下两种情况下:

    • 从包含package.json的目录中运行npm install
    • 在任何其他目录中运行npm install $package
  • devDependencies:

    • 在包含package.json的目录中运行npm install时,除非您传递了--production标志(请为Gayan Charith's answer投票),或者设置了NODE_ENV=production环境变量,否则也会被安装
    • 在任何其他目录中运行npm install "$package"时,除非您使用--dev选项,否则不会被安装
    • 没有被传递安装。
  • peerDependencies:

    • 在3.0之前:如果缺少,则始终会被安装,并引发错误,如果不同的依赖关系将使用多个不兼容版本的依赖项。
    • 预计从3.0开始(未经测试):如果在npm install中缺少,则会发出警告,您必须手动解决依赖关系。运行时,如果缺少依赖项,则会出现错误(由@nextgentech提到)。这很好地解释了这一点:https://flaviocopes.com/npm-peer-dependencies/
    • 在版本7中,peerDependencies会自动安装,除非存在无法自动解决的上游依赖关系冲突
  • 传递性(由Ben Hutchison提到):

    • dependencies会被传递安装:如果A需要B,B需要C,则会安装C,否则B可能无法工作,A也不行。

    • devDependencies不会被传递安装。例如,我们不需要测试B来测试A,因此可以省略B的测试依赖项。

这里没有讨论相关选项:

devDependencies

dependencies是必需的运行时依赖,devDependencies只用于开发,例如:单元测试、CoffeeScript到JavaScript的转换、缩小等。

如果您要开发一个包,可以下载它(例如通过git clone),进入其包含package.json的根目录,然后运行:

npm install

既然你有实际的源代码,那么你肯定想要开发它,所以默认情况下,安装了dependencies(因为你必须运行才能开发),以及devDependency依赖。

但是,如果你只是一个终端用户,只想安装一个软件包来使用它,你可以从任何目录执行以下操作:

npm install "$package"

在这种情况下,通常不需要开发依赖项,因此您只需获取使用软件包所需的内容:dependencies
如果您确实想在这种情况下安装开发软件包,可以将dev配置选项设置为true,可能需要从命令行执行:
npm install "$package" --dev

默认情况下,该选项为false,因为这是一个较少见的情况。

peerDependencies

(在3.0之前测试)

来源: https://nodejs.org/en/blog/npm/peer-dependencies/

对于常规依赖项,您可以有多个版本的依赖项:它只是安装在依赖项的node_modules内。

例如,如果dependency1dependency2都以不同的版本依赖于dependency3,则项目树将如下所示:

root/node_modules/
                 |
                 +- dependency1/node_modules/
                 |                          |
                 |                          +- dependency3 v1.0/
                 |
                 |
                 +- dependency2/node_modules/
                                            |
                                            +- dependency3 v2.0/

然而,插件是通常不需要其他包的软件包,这在这个上下文中被称为宿主。相反:

  • 插件需要由宿主
  • 插件提供宿主期望找到的标准接口
  • 只有宿主将直接被用户调用,因此必须有一个单独的版本。

例如,如果dependency1dependency2dependency3具有对等依赖关系,则项目树将如下所示:

root/node_modules/
                 |
                 +- dependency1/
                 |
                 +- dependency2/
                 |
                 +- dependency3 v1.0/

即使您在package.json文件中从未提及dependency3,这也会发生。
我认为这是控制反转设计模式的一个实例。
对等依赖的典型示例是Grunt、宿主和其插件。
例如,在像https://github.com/gruntjs/grunt-contrib-uglify这样的Grunt插件上,您将看到:
  • gruntpeer-dependency
  • 唯一的require('grunt')tests/下:它实际上并未被程序使用。
然后,当用户使用插件时,他将通过添加grunt.loadNpmTasks('grunt-contrib-uglify')行来隐式地从Gruntfile中要求插件,但用户将直接调用grunt
如果每个插件都需要不同的Grunt版本,则这将无法正常工作。

手册

我认为文档回答了问题,也许你只是不够熟悉node /其他包管理器。我可能只是因为我知道一些关于Ruby bundler的东西才能理解它。

关键的一行是:

这些内容将在从软件包的根目录执行npm link或npm install时安装,并且可以像任何其他npm配置参数一样进行管理。有关此主题的更多信息,请参见npm-config(7)。

然后在npm-config(7)下找到dev

Default: false
Type: Boolean

Install dev-dependencies along with packages.

9
啊,我明白了,我误解了。你的回答好像是npm install package是一个用来安装所有不是开发依赖项的包的命令,而不是我现在认为的意思,也就是“安装叫做[package]的包”,这也是我在阅读之前的理解方式。如果我是你,我会编辑成[package-name],这样就清楚地表明你的意思是“插入这里的名称”。 - Tom W
306
太好了!我之前没有意识到,但这个答案告诉我,dependencies和devDependencies的区别只适用于发布npm包。如果你只是在开发应用程序或网站,那它并不太重要。谢谢! - user677526
7
这篇文章应该更新,以反映在即将推出的npm@3中改变了“peerDependencies”的行为。来自http://blog.npmjs.org/post/110924823920/npm-weekly-5的消息:“我们将不再自动下载对等依赖项。相反,如果尚未安装对等依赖项,我们会发出警告。这需要您自己手动解决peerDependency冲突,但从长远来看,这应该会减少您的软件包依赖项出现问题的可能性。” - nextgentech
13
另外,依赖项(即dependencies)不会被依赖包(transitively)安装。例如,包A依赖于包B,而包B依赖于包C并且还在devDependencies中依赖于包D。如果你从包A运行npm install,你会得到B和C,但不会有D。 - Ben Hutchison
28
需要注意的是,当 NODE_ENV 设置为 production 时,devDependencies 不会被安装。 - Augusto Franzoia
显示剩余15条评论

634

如果你不想安装devDependencies,你可以使用 npm install --production 命令。


1
npm install --save 是用于软件依赖关系的吗? - Vamsi Pavan Mahesh
24
npm install将安装所有依赖项。当您想将特定模块添加到package.json中时,使用--save标志。例如:npm install uglify --save将在您的项目文件夹中安装uglify,并将uglify添加到项目的package.json文件中。 - Gayan Charith
7
我们正在谈论devDependencies,您可以使用--save-dev将新模块保存为devDependency。例如:npm install uglify --save-dev。 - Mykaelos
25
自从 npm 5 版本起,--save 选项不再需要了。如果你运行 "npm install my-package",它会将 my-package 添加到你的 package.json 文件中作为一个依赖项。 - Martin Carel
just npm install - Sultan Aslam
2
从npm 5开始,“--save”选项不再必要。这是个好消息!我以前并没有意识到这一点。我总是觉得非常烦人,大多数文档都会忽略“--save”选项,但实际上很少不保存添加的依赖项。 - Stijn de Witt

159

依赖项
指项目运行所需的依赖,比如提供从您代码中调用的函数的库。
它们是传递安装的 (如果 A 依赖于 B 依赖于 C,则在 A 上执行 npm 安装将安装 B 和 C)。
例如:lodash:您的项目调用一些 lodash 函数。

开发依赖项
只在开发或发布期间需要的依赖,比如将您的代码编译成 JavaScript 的编译器、测试框架或文档生成器。
它们不会被传递安装 (如果 A 依赖于 B dev-依赖于 C,则在 A 上执行 npm 安装将仅安装 B)。
例如:grunt:您的项目使用 grunt 来构建自身。

同行依赖项
指您的项目在父项目中钩入或修改的依赖,通常是其他库或工具的插件。它仅作为检查,确保父项目(依赖于您的项目的项目)对您钩入的项目有依赖关系。因此,如果您创建了添加到库 B 中的功能的插件 C,则制作项目 A 的人如果有 B 的依赖关系,则需要有 C 的依赖关系。
它们不会被安装(除非使用 npm < 3),只会被检查。
例如:grunt:您的项目为 grunt 添加了功能,只能用于使用 grunt 的项目。

此文档很好地解释了同行依赖: https://nodejs.org/en/blog/npm/peer-dependencies/

此外,npm 文档随着时间的推移得到了改进,现在更好地解释了不同类型的依赖项: https://github.com/npm/cli/blob/latest/docs/content/configuring-npm/package-json.md#devdependencies


9
应该将其标记为“答案”——清晰简洁,位于“peerDependencies”下的第一句话正确地解释了所有内容,无需深入探讨npm中版本如何解析,这并不是理解peerDependencies的必要内容。 - fires3as0n
3
终于有一个对peerDependencies的回答,满足了“为什么?”而不是“如何?”非常有帮助! - Joe Fitzsimmons
2
清晰明确的peerDependencies!谢谢。 - Girish Ingle

150

以示例说明,mocha通常应是devDependency,因为测试在生产环境中并不必要,而express应该是dependency。


5
我倾向于将测试作为依赖项,因为在启动生产服务器之前,您可能希望运行自测。 - user9903
61
我建议使用像Hudson或CircleCI这样的持续集成服务,它可以运行您的测试,如果测试通过,则部署到生产环境。 - Dan Kohn
1
仍然有必要测试实际服务器,因为CI服务器可能会与生产服务器有所不同,而这种差异可能会阻止应用程序启动... - Nicole
2
@Nicole,为什么你会让你的暂存服务器与生产环境的配置不相同呢? - Lucas
2
然而,将测试依赖项作为常规依赖项添加会引入许多额外的库,其中每个库都可能以某种方式失败。我倾向于使用尽可能少的代码的轻量级生产服务器。记住,最好的代码是没有代码! - Stijn de Witt
显示剩余3条评论

81

将一个包保存为 dev 依赖项到 package.json

npm install "$package" --save-dev

当您运行npm install时,它会安装devDependenciesdependencies。如果要避免安装devDependencies,请运行:

npm install --production

5
可以使用以下命令: npm i -S 该命令将安装所需的包并将其添加到项目的 package.json 文件中。 - maysara

47

有一些模块和包仅在开发过程中需要,而在生产环境中则不需要。就像文档中所说的那样:(文档链接)

如果别人想要下载和使用你的模块,并将其纳入他们的程序中,那么他们可能不需要也不想要下载和构建你所使用的外部测试或文档框架。在这种情况下,最好将这些额外项列在devDependencies哈希表中。


1
如果您只在生产环境中运行bundle.js文件,那么您真的需要这些依赖关系吗? - RegarBoy
如果您在服务器上运行bundle.js,则正在进行服务器端的webpack或其他操作...请检查是否是这种情况,因为通常不是这样,并且实际上需要大量工作才能正确运行(我知道,因为我曾经这样做过)。我怀疑您的bundle.js只是提供给浏览器并包含客户端代码。 - Stijn de Witt
1
让我困惑的是,这里的“production”是什么?是我部署构建的CDN服务器还是运行我的构建的客户端浏览器? - Viraj Singh

30

peerDependencies对我来说不太容易理解,直到我读到一篇博客文章上关于Ciro上面提到的这个主题的片段:

插件需要一种表达插件与其宿主包之间“依赖关系”的方法。某种方式表达:“只有当我插入到我的宿主包版本1.2.x中时才能工作,因此如果您安装我,请确保它与兼容的宿主一起使用。”我们称这种关系为同级依赖。

插件确实期望主机的特定版本...

peerDependencies适用于插件,即需要一个“主机”库才能执行其功能的库,但可能是在最新版本的主机发布前编写的。

也就是说,如果我为HostLibraryX v3编写PluginX v1并离开,那么不能保证PluginX v1在发布HostLibraryX v4(甚至是HostLibraryX v3.0.1)时仍能正常工作。

... 但插件并不依赖于主机...

从插件的角度来看,它仅向主机库添加功能。实际上,插件通常并不真正“依赖”于其主机,因此我并不需要主机添加插件的依赖项。如果没有主机,插件将无害地不执行任何操作。
这意味着“依赖项”并不是插件的正确概念。
更糟糕的是,如果我的主机被视为依赖项,我们将陷入同一篇博客文章提到的情况(稍作编辑以使用本答案中虚构的主机和插件)。

But now, [if we treat the contemporary version of HostLibraryX as a dependency for PluginX,] running npm install results in the unexpected dependency graph of

├── HostLibraryX@4.0.0
└─┬ PluginX@1.0.0
  └── HostLibraryX@3.0.0

I’ll leave the subtle failures that come from the plugin using a different [HostLibraryX] API than the main application to your imagination.

......而且主机显然不依赖于插件...

...这就是插件的全部意义。现在,如果主机足够友好地包含了{{所有}}插件的依赖信息,那么问题就解决了,但是这也会引入一个巨大的新文化问题:插件管理!

插件的整个意义在于它们可以匿名配对。在完美的世界中,让主机管理所有插件将会整洁而方便,但我们不会要求库来驱动猫。

如果我们没有层次依赖关系,也许我们是相互依赖的同级...

相反,我们有成为同级的概念。主机和插件都不在对方的依赖桶中。两者都生存在依赖图的同一级别。


...但这不是一种可自动化的关系。<<< Moneyball!!!

如果我是PluginX v1,并且期望一个同伴(即具有peerDependency ofHostLibraryX v3,我会这样说。如果你已经自动升级到最新的HostLibraryX v4(注意这是版本4),并且安装了Plugin v1,你需要知道,对吧?

npm无法为我管理这种情况--

"嘿,我看到你正在使用PluginX v1!我将自动将HostLibraryX从v4降级到v3,好吗?"

...或者...

"嘿,我看到你正在使用PluginX v1。它期望HostLibraryX v3,但你在上次更新中已经把它甩在了身后。为了安全起见,我将自动卸载Plugin v1!!1!

怎么样,npm?不要这样做。

因此,npm不会这样做。它会提醒您这种情况,并让您确定HostLibraryX v4是否适合Plugin v1的同伴。


Coda

在插件中良好的peerDependency管理将使这个概念在实践中更加直观。来自博客文章...

一个建议:与常规依赖关系不同,对等依赖关系的要求应该是宽松的。你不应该将你的对等依赖关系锁定到特定的修补程序版本。如果一个Chai插件对Chai 1.4.1有对等依赖,而另一个插件对Chai 1.5.0有对等依赖,那么这将非常令人恼火,因为作者们懒得花时间找出它们与Chai实际兼容的最小版本。


我还是不明白这个论点,即插件不是“依赖”而是“期望”宿主包——这两者有什么区别呢?如果只是插件不需要/导入宿主库,那似乎只是个技术上的细节,可以说插件仍然依赖它,显然它仍然依赖(通过实现)它所定义的“接口”。 - undefined
@AloisMahdal 你使用人类的逻辑和定义来解释这些词,但我们在这里必须专业化。如果libDependsV2依赖于libRequirementV3,在npm领域中,只要package.json中有libDependsV2,libRequirementV3就会被安装。但我们不希望插件也是这样。关键在于这句话:“如果没有宿主,插件不会产生任何影响。”如果插件没有“优雅地失败”,我们就会遇到问题。但插件期望与宿主建立对等关系,并且只有在找到兼容的宿主时才“激活”。否则,你就会遇到我上面描述的管理问题。 - undefined
回顾一下“...但插件不依赖于主机...”部分。这是一个微妙(以“小”为例)的区别,如果你愿意,你可以编写一个使用传统依赖关系的“插件”,但这种紧密耦合正是peerDependencies试图解决的问题。也就是说,你的场景已经处理过了,但如果我们不想使用紧密耦合的依赖关系,我们该怎么办呢?我们将获得更多的灵活性,但npm将无法自动执行这种关系的强制性。如果这符合你的需求,peerDependencies可以让你实现这一点。 - undefined
最后,这篇博客提出了一个很好的观点:“一个建议是,与常规依赖不同,对于对等依赖的要求应该宽松一些。” peerDependencies 的作用是允许一个“规范之外”的插件在 libRequirementV4 发布后仍然存在。如果 libDependsV2 能够自动适配新版本,那就太完美了!但是,由于我们告诉了 npm,“这不是你要强制执行的关系”,npm只能“向你提醒这种情况,并让你判断 HostLibraryX v4 是否适合作为 Plugin v1 的对等依赖。” - undefined

25

一个简单的让我更加清楚的解释是:

当你部署你的应用时,依赖中的模块需要被安装,不然你的应用就无法工作。而 devDependencies 中的模块不需要在生产服务器上安装,因为你没有在那台机器上开发。

链接

3
所以,如果我们正在制作网站,并且在生产版本中所有库都将被内联到vendor.js中,那么如果编译后的代码提交到存储库中,所有依赖项都应该是开发依赖项?而且它应该被提交,否则你不仅需要安装模块,还需要编译它(因为任何子模块的更改都可能导致回归测试)... - Qwertiy
很棒的回答,但是还有一个问题:Webpack可能会构建出损坏的包吗?我猜测在生产环境中devDependencies模块将不可用,我的意思是webpack -p。请回答我的问题。 - AmerllicA
如果在生产构建过程中出现任何问题,您的部署流程应该设计成在构建时显示错误,并且不会将损坏的代码推送到生产环境(例如,您可以尝试使用Jenkins)。无论如何,devdependencies不需要安装在生产服务器上。 - Jyoti Duhan

24

我找到了一个简单的解释。

简短回答:

dependencies "...是你的项目在生产环境中真正需要的依赖项。"

devDependencies "...是你在开发过程中需要的依赖项。"

peerDependencies "如果你想创建和发布自己的库,以便它可以作为依赖项使用。"

更多细节请参阅此帖子: https://code-trotter.com/web/dependencies-vs-devdependencies-vs-peerdependencies


顶级答案非常详细和详尽,但这个答案用通俗易懂的方式解释了 peerDependencies - Tafadzwa Gonera

20

我想补充一下对这些依赖关系解释的看法

  • dependencies 用于您代码库中直接使用的部分,通常最终会在生产代码或代码块中使用
  • devDependencies 用于构建过程中使用的工具,帮助您管理最终代码的生成方式,包括第三方测试模块(例如webpack等工具)

CSS资产怎么样? - Brian Zelip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接