npm package.json 文件中的依赖关系,devDependencies 和 peerDependencies 之间有什么区别?

该文档很难回答我的问题。我不明白那些解释。有人可以用简单的话说吗?如果很难选择简单的单词,也许还有例子?

EDIT还添加了peerDependencies ,这是密切相关的,可能会引起混乱。

答案

重要行为差异摘要:

  • dependencies都安装在两个上:

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

    • 也安装在npm install在包含package.json的目录上,除非您传递--production标志(请赞成 Gayan Charith 的 answer )。
    • 没有安装在npm install "$package"上,除非您给它提供--dev选项,否则请在其他任何目录上npm install "$package"
    • 不能临时安装。
  • peerDependencies

    • 3.0 之前的版本:如果缺少则始终安装,如果不同的依赖项使用多个不兼容的依赖项版本,则会引发错误。
    • 预期从 3.0 开始 (未试用):如果npm install缺少该警告,则发出警告,您必须手动解决依赖关系。运行时,如果缺少依赖项,则会出现错误( @nextgentech提到)
  • 可传递性(由Ben Hutchison提及):

    • dependencies是通过传递方式安装的:如果 A 需要 B,而 B 需要 C,则 C 将被安装,否则 B 无法工作,A 也将无法工作。

    • devDependencies不会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/

但是,插件是通常不需要其他软件包的软件包,在此上下文中,该软件包称为主机 。代替:

  • 主机需要插件
  • 插件提供了主机希望找到的标准接口
  • 用户只能直接调用主机,因此必须有一个单独的版本。

例如,如果dependency1dependency2对等依赖于dependency3 ,则项目树将如下所示:

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/ :程序并未实际使用它。

然后,当用户将使用插件,他会隐需要从插件Gruntfile通过添加grunt.loadNpmTasks('grunt-contrib-uglify')线,但它的grunt ,用户将直接调用。

如果每个插件都需要不同的 Grunt 版本,则无法使用。

手册

我认为文档很好地回答了这个问题,也许您对节点 / 其他包管理器还不够熟悉。我可能只了解它,因为我对 Ruby bundler 有所了解。

关键是:

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

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

Default: false
Type: Boolean

Install dev-dependencies along with packages.

如果您不想安装 devDependencies,则可以使用npm install --production

例如,mocha 通常是 devDependency,因为在生产中不需要测试,而 express 是依赖项。

要将软件包作为 dev 依赖项保存到package.json

npm install "$package" --save-dev

当您运行npm install ,它将同时安装devDependenciesdependencies 。为了避免安装devDependencies运行:

npm install --production

依存关系
您的项目需要运行的依赖项,例如提供从代码中调用的函数的库。
它们是可传递安装的(如果 A 依赖于 B 依赖于 C,则在 A 上进行 npm install 将安装 B 和 C)。
示例:lodash:您的项目调用了一些 lodash 函数。

devDependencies
您仅在开发或发布过程中需要依赖项,例如将代码带入 javascript,测试框架或文档生成器的编译器。
它们不是临时安装的(如果 A 依赖于 B,而开发人员依赖于 C,则在 A 上进行 npm install 只会安装 B)。
示例:grunt:您的项目使用 grunt 进行构建。

peerDependencies
您的项目在父项目(通常是某些其他库或工具的插件)中挂钩或修改的依赖项。它仅是一种检查,请确保父项目(将取决于您的项目)对您挂接到的项目具有依赖性。因此,如果您制作了一个向库 B 添加功能的插件 C,那么制作项目 A 的某人如果对 C 有依赖性,就必须对 B 有依赖性。
它们未安装(除非 npm <3),仅对其进行检查。
示例:grunt:您的项目为 grunt 添加了功能,并且只能在使用 grunt 的项目上使用。

该文档非常好地解释了对等方的依赖性: https : //nodejs.org/en/blog/npm/peer-dependencies/

另外,随着时间的推移,npm 文档也得到了改进,现在对不同类型的依赖项有了更好的解释: https : //github.com/npm/cli/blob/latest/doc/files/package.json.md#devdependencies

有些模块和软件包仅对于开发是必需的,而在生产中则不需要。就像它在文档中说的那样:

如果有人计划在其程序中下载和使用您的模块,则他们可能不想或不需要下载并构建您使用的外部测试或文档框架。在这种情况下,最好在 devDependencies 哈希中列出这些其他项。

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

部署应用程序时,需要安装依赖项中的模块,否则您的应用程序将无法运行。无需在生产服务器上安装 devDependencies 中的模块,因为您不在该计算机上进行开发。 链接

我想对这些依赖关系的解释加我的看法

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

在我从上面提到的有关Ciro 的博客文章中阅读了以下代码段之前, peerDependencies对我来说意义不大:

[ plugins ] 需要的是一种表达插件及其主机包之间的 “依赖关系” 的方式。可以这样说:“我只有在插入主机软件包的 1.2.x 版本时才能工作,因此,如果您安装了我,请确保它与兼容主机并排。” 我们将此关系称为对等依赖项。

该插件是否期待主机的特定版本...

peerDependencies用于插件,它们是需要 “主机” 库来执行其功能的库,但可能是在发布最新版本的主机之前编写的。

也就是说,如果我为HostLibraryX v3编写PluginX v1并走开,则不能保证在发布HostLibraryX v4 (甚至HostLibraryX v3.0.1 )时HostLibraryX v4 PluginX v1会工作。

... 但是该插件不依赖于主机...

从插件的角度来看,它仅向主机库添加功能。我并不是真的 “需要” 主机向插件添加依赖项,而插件通常实际上并不依赖于其主机。如果您没有主机,则该插件不会执行任何操作。

这意味着dependencies并不是插件的正确概念。

更糟糕的是,如果将我的主机视为依赖项,我们最终会遇到同一篇博客文章提到的情况 (稍作编辑以使用此答案的主机和插件组成):

但是现在,[如果我们将当代版本的 HostLibraryX 视为 PluginX 的依赖项,则运行npm install导致意外的依赖关系图

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

我将使用与主应用程序不同的 [HostLibraryX] API 来解决由插件引起的细微故障,这是您的想象力。

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

... 这就是插件的全部重点。现在,如果主机足够好,可以为其所有插件添加依赖项信息,那么就可以解决问题,但这也将带来一个巨大的新文化问题 :插件管理!

插件的全部目的是它们可以匿名配对。在一个理想的世界中,让主人来管理它们会很整洁,但是我们不会要求图书馆放牧猫。

如果我们不是层次依赖的,也许我们是内部依赖的同行...

相反,我们有成为同行的概念。主机和插件都不位于另一个的依赖项存储桶中。两者都生活在依赖图的同一级别上。

... 但这不是自动的关系。

如果我是PluginX v1并且希望 HostLibraryX v3建立对等关系(即peerDependency 为 ),那么我会这样说。如果你已经自动升级到最新HostLibraryX v4 (注意的第 4),并Plugin v1安装,你需要知道,对不对?

npm无法为我解决这种情况 -

“嘿,我看到您正在使用PluginX v1 !我会自动将HostLibraryX从 v4 降级到 v3,kk?”

... 要么...

“嘿,我看到您正在使用PluginX v1 。希望您能在上一次更新时将HostLibraryX v3留在尘土中。为了安全起见,我会自动卸载Plugin v1 !! 1!

不,npm ?!

所以 npm 不会。它会提醒您这种情况,并让您确定HostLibraryX v4是否适合Plugin v1


结尾

插件中良好的peerDependency管理将使该概念在实践中更加直观。从博客文章中 ,又一次...

建议之一:与常规依赖项不同,对等依赖项要求应宽松。您不应将对等依赖项锁定到特定的补丁程序版本。如果一个 Chai 插件同等地依赖 Chai 1.4.1,而另一个依赖 Chai 1.5.0,那真是令人讨厌,这仅仅是因为作者很懒,并且没有花时间确定他们的实际最小 Chai 版本。兼容。

简而言之

  1. 依赖关系 npm install <package> --save-prod在生产环境中安装应用程序所需的软件包。

  2. DevDependencies - npm install <package> --save-dev安装仅本地开发和测试所需的软件包

  3. 只需输入npm install安装 package.json 中提到的所有软件包。

因此,如果您在本地计算机上工作,只需键入npm install并继续:)