为什么要在npm中使用对等依赖性插件?


217

例如,为什么Grunt插件将其对grunt的依赖关系定义为“ 对等依赖关系 ”?

为什么插件不能仅在Grunt -plug / node_modules中将 Grunt作为其依赖

对等依赖项在此处进行了描述:https : //nodejs.org/en/blog/npm/peer-dependencies/

但是我真的不明白。

目前,我正在使用AppGyver Steroids,它使用Grunt任务将我的源文件构建到/ dist /文件夹中,以便在本地设备上提供服务。我在npm上很新,很咕unt,所以我想完全理解发生了什么。

到目前为止,我得到了:

[rootfolder] /package.json告诉npm它依赖于grunt-steroidsnpm包进行开发:

  "devDependencies": {
    "grunt-steroids": "0.x"
  },

好的。在[rootfolder]中运行npm install 可以检测依赖性,并在[rootfolder] / node_modules / grunt-steroids中安装grunt-steroids

Npm然后读取[rootfolder] /node_modules/grunt-steroids/package.json,以便它可以安装grunt-steroids自己的依赖项。

"devDependencies": {
    "grunt-contrib-nodeunit": "0.3.0",
    "grunt": "0.4.4"
  },
"dependencies": {
    "wrench": "1.5.4",
    "chalk": "0.3.0",
    "xml2js": "0.4.1",
    "lodash": "2.4.1"
  },
"peerDependencies": {
    "grunt": "0.4.4",
    "grunt-contrib-copy": "0.5.0",
    "grunt-contrib-clean": "0.5.0",
    "grunt-contrib-concat": "0.4.0",
    "grunt-contrib-coffee": "0.10.1",
    "grunt-contrib-sass": "0.7.3",
    "grunt-extend-config": "0.9.2"
  },

依赖 ”软件包安装在[rootfolder] / node_modules / grunt-steroids / node_modules中,这对我来说很合理。

未安装“ devDependencies ”,我确定这是由npm检测控制的,即我只是在尝试使用grunt-steroids而不是在上面进行开发。

但是然后我们有了“ peerDependencies ”。

这些安装在[rootfolder] / node_modules中,我不明白为什么在[rootfolder] / node_modules / grunt-steroids / node_modules中而不安装在其中,从而避免了与其他grunt插件(或其他)的冲突?

Answers:


420

TL; DR:[1] peerDependencies用于暴露于使用方代码(并预计将由使用方代码使用)的依赖关系,而不是“私有”依赖项,而不是未公开的依赖项,仅是实现细节。

对等依赖性问题解决

NPM的模块系统是分层的。简单方案的一大优势是,当您安装npm软件包时,该软件包会带来自己的依赖关系,因此可以立即使用。

但是,在以下情况下会出现问题:

  • 您的项目和正在使用的某个模块都依赖于另一个模块。
  • 这三个模块必须互相交谈。

在例子中

假设您正在构建,YourCoolProject并且同时使用JacksModule 1.0JillsModule 2.0。让我们假设这JacksModule也取决于JillsModule,但是取决于不同的版本,比如说1.0。只要这两个版本不符合要求,就没有问题。在表面之下JacksModule使用的事实JillsModule仅仅是实现细节。我们捆绑了JillsModule两次,但是当我们获得开箱即用的稳定软件时,这是一个很小的代价。

但是现在如果 JacksModuleJillsModule以某种方式公开其依赖性的话。例如,它接受一个实例JillsClass...当我们创建该库的new JillsClass使用版本2.0并将其传递给时,会发生jacksFunction什么?所有的地狱都会挣脱!诸如此类的简单事情jillsObject instanceof JillsClass会突然返回,false因为jillsObject实际上是另一个 实例JillsClass2.0版本。

对等依赖性如何解决这个问题

他们告诉npm

我需要这个软件包,但是我需要项目中包含的版本,而不是模块私有的版本。

当npm看到您的软件包正在安装到具有该依赖项或具有不兼容版本的项目中时,它将在安装过程中向用户发出警告。

什么时候应该使用对等依赖关系?

  • 当您构建供其他项目使用的库时,以及
  • 该库正在使用其他库,并且
  • 您希望/也需要用户与该其他库一起使用

常见方案是适用于大型框架的插件。想想诸如Gulp,Grunt,Babel,Mocha等之类的东西。如果编写Gulp插件,则希望该插件与用户项目所使用的Gulp一起使用,而不是与自己的Gulp私有版本一起使用。


注解

  1. 太长; 没看。用于表示人们认为过长的文本的简短摘要。

2
我注意到并且在任何地方都没有说过的一件重要事情,当我们构建一个插件时,我们是否应该有一个包依赖关系的副本,而对等依赖关系呢?在OP示例中,我们可以看到这"grunt": "0.4.4"既存在于devDependencies中,又存在于peerDependencies中,在那儿有一个副本确实是有意义的,因为这意味着我既需要该grunt程序包供自己使用,也需要我的用户使用只要遵守peerDependencies版本锁定,库就可以使用自己的版本。那是对的吗?还是OP的例子很糟糕?
Vadorequest

4
我可以想象人们创建的Grunt插件是Grunt的拥护者:)因此,对于他们来说,在他们的插件的构建过程中使用Grunt似乎很自然...。但是为什么他们要锁定他们的Grunt版本范围呢?他们用来创建它的构建过程?将其添加为开发依赖项可以使他们解耦。基本上有两个阶段:构建时间和运行时间。在构建期间需要Dev依赖项。运行时需要常规和对等依赖项。当然,由于依赖关系的依赖,一切都会很快变得混乱:)
Stijn de Witt

1
感谢您的回答!只是为了澄清,在你的榜样,如果JacksModule上取决于JillsModule ^1.0.0JillsModule是的对等的依赖JacksModuleYourCoolProject使用JacksModule,并JillsModule ^2.0.0,我们将获得由NPM同行依赖关系的警告,他们会建议我们安装JillsModule ^1.0.0为好。但是那会发生什么呢?YourCoolProject现在将有两个JillsModule可通过以下版本导入的版本import jillsModule from "..."吗?而且我怎么还记得在使用时JacksModule需要传递一个实例JillsModule v1.0.0
tonix

1
@tonix好吧,您的版本不兼容确实是一个问题。peerDependencies不能解决这个问题。但这确实有助于使问题明确。因为它将清楚地显示版本不匹配,而不是静默使用两个版本。选择库的应用程序开发人员将必须找到解决方案。
Stijn de Witt

2
@tonix或第三个选项:克隆存储JacksModule库,对其进行升级以使其依赖,JillsModule ^2.0.0并向项目维护者提供PR。首先声明此依赖项已过时,您可能需要提交一个错误,您希望帮助对其进行更新。如果您获得良好的PR,大多数图书馆维护者都会将其合并并表示感谢。如果维护人员没有响应,则可以将叉子发布到以您的名字命名的NPM并使用叉子。无论如何,都有解决方案,但peerDependencies不能单独解决。
Stijn de Witt

26

我建议您先重新阅读该文章。有点令人困惑,但是带有winston-mail的示例向您显示了答案:

例如,让我们假装在其对象中winston-mail@0.2.3指定"winston": "0.5.x""dependencies"对象,因为这是对其进行测试的最新版本。作为应用程序开发人员,您需要最新最好的东西,因此您要查找winston和的最新版本,winston-mail然后将它们放入package.json中,如下所示:

{
  "dependencies": {  
    "winston": "0.6.2",  
    "winston-mail": "0.2.3"  
  }  
}

但是现在,运行npm install会导致意外的依赖关系图

├── winston@0.6.2  
└─┬ winston-mail@0.2.3                
  └── winston@0.5.11

在这种情况下,可能有一个软件包的多个版本,这会引起一些问题。对等依赖性使npm开发人员可以确保用户具有特定的模块(在根文件夹中)。但是,您的观点是正确的,即描述软件包的一个特定版本会导致使用其他版本的其他软件包出现问题。如文章所述,此问题与npm开发人员有关

建议之一:与常规依赖项不同,对等依赖项要求应宽松。您不应将对等依赖项锁定到特定的补丁程序版本。

因此,开发人员应遵循semver定义peerDependencies。您应该在GitHub上打开grunt-steroids软件包的问题...


1
您说的是,multiple versions of a package which would cause some issues但这不是包管理器的重点吗?他们甚至在同一篇文章中进一步讨论了该问题,其中项目中有相同软件包的两个版本:一个由开发人员提供,一个由第三方库提供。
亚当·贝克

1
我想我了解对等方依赖的要点,但是在winston示例中我现在无法使用该winston-mail库,因为我的版本与对等方依赖不匹配?我宁愿暂时将最新版本和最大版本对1个库进行一次降级,也不想完全不使用它。
亚当·贝克

1
就我的第一条评论而言,据我了解和使用,它与测试有关,例如,如果您有已针对特定的第三方包装测试过的包装,则无法确定软件包将起作用的依赖项更改(错误修复,主要功能更新)。因此,您可以指定特定的插件版本并随测试一起保存。
Fer To

1
关于您的第二条评论:这就是为什么他们在文档中说开发人员应宽容其程序包依赖关系,并应使用semver,例如,代替“ 0.2.1”,“〜0.2.1”->允许使用“ 0.2.x”,但不是“ 0.3.x”或“> = 0.2.1”->从“ 0.2.x”到“ 1.x”或“ x.2。”的所有内容。..(但对于npm软件包,并不是很可取,它会与〜一起使用
Fer To

15

peerDependencies 用最简单的示例进行解释:

{
  "name": "myPackage",
  "dependencies": {
    "foo": "^4.0.0",
    "react": "^15.0.0"
  }
}


{
  "name": "foo"
  "peerDependencies": {
    "react": "^16.0.0"
  }
}

在myPackage中运行npm install会引发错误,因为它试图安装React版本^15.0.0AND foo并且仅与React兼容^16.0.0

没有安装peerDependencies。


为什么不把foo里的dep 16作为dep?这样15和16都将可用,并且foo可以使用16,而mypackage可以使用15?
nitinsh99

React是一个在运行时进行引导的框架,为了使React 15和React 16都存在于同一页面上,您将需要同时对它们进行增强处理,这对于最终用户而言将是非常沉重且成问题的。如果同时foo适用于React 15和React 16,则可以将其peerDependency列为>=15 < 17
詹斯·鲍德

nitinsh99我的答案是用尽可能简单的示例来说明peerDependencies的目的,而不是如何摆脱peerDependencies引发的错误
Christopher Tokar,

@ nitinsh99在包依赖项内添加react将提供类似Hooks的问题-包中有多个reacts
Masood
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.