例如,为什么一个Grunt插件将它对Grunt的依赖定义为“对等依赖”?

为什么插件不能把Grunt作为它自己的依赖项放在Grunt -plug/node_modules中?

对等依赖关系描述在这里:https://nodejs.org/en/blog/npm/peer-dependencies/

但我不太明白。

例子

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

到目前为止,我得到了这个:

[rootfolder] /包。Json告诉NPM它依赖于NPM包的grunt-steroids进行开发:

  "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插件(或任何东西)的冲突?


当前回答

我建议你先把文章再读一遍。这有点令人困惑,但winston-mail的例子告诉了你为什么:

For example, let's pretend that winston-mail@0.2.3 specified "winston": "0.5.x" in its "dependencies" object because that's the latest version it was tested against. As an app developer, you want the latest and greatest stuff, so you look up the latest versions of winston and of winston-mail and put them in your package.json as { "dependencies": { "winston": "0.6.2", "winston-mail": "0.2.3" } } But now, running npm install results in the unexpected dependency graph of ├── winston@0.6.2 └─┬ winston-mail@0.2.3 └── winston@0.5.11

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

一条建议:对等依赖要求,不像那些常规依赖,应该是宽松的。您不应该将您的对等依赖关系锁定到特定的补丁版本。

因此,开发人员应该遵循semver定义peerDependencies。你应该在GitHub上为咕噜类固醇包打开一个问题…

其他回答

用最简单的例子解释了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.0和foo,这只与React ^16.0.0兼容。

peerDependencies未安装。

我建议你先把文章再读一遍。这有点令人困惑,但winston-mail的例子告诉了你为什么:

For example, let's pretend that winston-mail@0.2.3 specified "winston": "0.5.x" in its "dependencies" object because that's the latest version it was tested against. As an app developer, you want the latest and greatest stuff, so you look up the latest versions of winston and of winston-mail and put them in your package.json as { "dependencies": { "winston": "0.6.2", "winston-mail": "0.2.3" } } But now, running npm install results in the unexpected dependency graph of ├── winston@0.6.2 └─┬ winston-mail@0.2.3 └── winston@0.5.11

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

一条建议:对等依赖要求,不像那些常规依赖,应该是宽松的。您不应该将您的对等依赖关系锁定到特定的补丁版本。

因此,开发人员应该遵循semver定义peerDependencies。你应该在GitHub上为咕噜类固醇包打开一个问题…

peerDependencies用于向消费代码公开(并期望被使用)的依赖项,与未公开且仅是实现细节的“私有”依赖项相反。

对等依赖解决的问题

NPM的模块系统是分层的。对于更简单的场景,一个很大的优势是当你安装一个npm包时,这个包自带它自己的依赖关系,所以它可以开箱即用。

但问题出现在:

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

在示例

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

但是现在如果JacksModule以某种方式暴露了它对JillsModule的依赖呢?它接受JillsClass的一个实例,例如…当我们使用库的2.0版本创建一个新的JillsClass并将其传递给jacksFunction时会发生什么?一切都会乱套的!像jillsObject instanceof JillsClass这样简单的东西会突然返回false,因为jillsObject实际上是另一个JillsClass(2.0版本)的实例。

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

它们告诉npm

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

当npm看到你的包被安装到一个不具有该依赖关系的项目中,或者有一个不兼容的版本时,它会在安装过程中警告用户。

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

当您正在构建供其他项目使用的库时,并且 这个库正在使用另一个库,并且 您期望/需要用户也使用其他库

常见的场景是大型框架的插件。比如Gulp, Grunt, Babel, Mocha等。如果你写了一个Gulp插件,你希望这个插件与用户的项目使用的是同一个Gulp,而不是你自己的Gulp私有版本。