ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

但我真的不明白。

例子

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

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

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

  "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 插件(或其他)冲突?


t
temporary_user_name

TL;DR: peerDependencies 用于暴露给消费代码(并预期被消费代码使用)的依赖项,而不是 “私有” 依赖项暴露,并且只是一个实现细节。

对等依赖关系解决的问题

NPM 的模块系统是分层的。更简单场景的一大优势是,当您安装 npm 包时,该包会自带依赖项,因此它可以开箱即用。

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

您的项目和您正在使用的某个模块都依赖于另一个模块。

这三个模块必须相互通信。

在示例中

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

但是现在如果 JacksModule 以某种方式暴露了它对 JillsModule 的依赖会怎样。例如,它接受 JillsClass 的实例...当我们使用库的版本 2.0 创建 new JillsClass 并将其传递给 jacksFunction 时会发生什么?所有的地狱都会崩溃! jillsObject instanceof JillsClass 之类的简单事物会突然返回 false,因为 jillsObject 实际上是 另一个 JillsClass 的实例,即 2.0 版本。

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

他们告诉 npm

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

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

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

当您构建一个供其他项目使用的库时,以及

该库正在使用其他库,并且

您期望/需要用户也可以使用其他库

常见的场景是大型框架的插件。想想 Gulp、Grunt、Babel、Mocha 等。如果您编写 Gulp 插件,您希望该插件与用户项目正在使用的 Gulp 一起工作,而不是与您自己的 Gulp 私有版本一起工作。


一件重要的事情我注意到并且没有在任何地方说,当我们构建一个插件时,我们是否应该有一个包依赖项的副本,用于对等依赖项?在 OP 示例中,我们可以看到 "grunt": "0.4.4" 位于 devDependencies 和 peerDependencies 中,我认为在其中有一个副本确实有意义,因为这意味着我需要该 grunt 包供我自己使用,但是我的图书馆的用户也可以使用他们自己的版本,只要它尊重 peerDependencies 版本锁定。那是对的吗?还是 OP 示例是一个非常糟糕的示例?
我可以想象创建 Grunt 插件的人是 Grunt 的粉丝 :) 因此,他们自己使用 Grunt 来构建插件的过程似乎很自然……但是他们为什么要锁定 Grunt 版本范围他们的插件可以工作与他们用来创建它的构建过程有关吗?将其添加为开发依赖项允许他们解耦。基本上有两个阶段:构建时间和运行时间。在构建期间需要开发依赖项。运行时需要常规和对等依赖项。当然,由于依赖关系的依赖,一切都会很快变得混乱:)
谢谢你的回答!只是为了澄清一下,在您的示例中,如果 JacksModule 依赖于 JillsModule ^1.0.0,其中 JillsModuleJacksModule 的对等依赖项,并且 YourCoolProject 使用 JacksModuleJillsModule ^2.0.0,我们将收到对等依赖项警告由 NPM 提供,它会建议我们也安装 JillsModule ^1.0.0。但那会发生什么呢? YourCoolProject 现在将有两个版本的 JillsModule 可通过 import jillsModule from "..." 导入?我怎么记得当我使用 JacksModule 时我需要传递一个 JillsModule v1.0.0 的实例?
@tonix好吧,您的版本不兼容确实是个问题。 peerDependencies 不能解决这个问题。但它确实有助于明确问题。因为它会清楚地显示版本不匹配,而不是默默地使用两个版本。选择库的应用程序开发人员必须找到解决方案。
@tonix 或者第三个选项:克隆 JacksModule 存储库,将其升级为依赖于 JillsModule ^2.0.0 并向项目维护者提供 PR。首先提交一个错误说明此依赖项已过时并且您想帮助更新它可能会有所帮助。如果你做了一个好的 PR,大多数库维护者会合并它并感谢你。如果维护者没有响应,你可以将你的 fork 发布到以你的名字命名的 NPM 并使用你的 fork。无论如何,有解决方案,但 peerDependencies 不能自行解决。
A
Andria

我建议你先再读一遍这篇文章。这有点令人困惑,但 winston-mail 的示例向您展示了原因:

例如,假设 winston-mail@0.2.3 在其“依赖项”对象中指定了“winston”:“0.5.x”,因为这是对其进行测试的最新版本。作为应用程序开发人员,您想要最新最好的东西,因此您查找 winston 和 winston-mail 的最新版本并将它们作为 { "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 包打开一个问题...


您说的是 multiple versions of a package which would cause some issues,但这不是包管理器的全部意义吗?他们甚至在同一篇文章中进一步讨论了这一点,其中项目中有同一个包的 2 个版本:一个由开发人员提供,一个由 3rd 方库提供。
我想我理解对等依赖的意义,但在 winston 示例中,我现在是否无法使用 winston-mail 库,因为我的版本与对等依赖不匹配?我宁愿将 1 库的最新和最好的临时降级,也不愿根本无法使用它。
对于您的第一条评论,据我了解和使用它,它与测试有关,例如,如果您有一个经过您针对特定第 3 方包测试的包,您不能确定如果一个您的依赖项更改(错误修复,主要功能更新),您的包将起作用。因此,您可以指定特定的插件版本并与您的测试一起保存。
关于您的第二条评论:这就是为什么他们在文档中说开发人员应该对他们的包依赖关系宽容并应该使用 semver,例如,而不是“0.2.1”,“~0.2.1”-> 允许“0.2.x”但是不是 "0.3.x" 或 ">=0.2.1" -> 从 "0.2.x" 到 "1.x" 或 "x.2." 的所有内容。 ..(但对于一个 npm 包来说并不是真的更可取~
A
Alex Lomia

peerDependencies 用最简单的例子解释:

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


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

在 myPackage 中运行 npm install 将引发错误,因为它正在尝试安装仅与 React ^16.0.0 兼容的 React 版本 ^15.0.0 AND foo

未安装 peerDependencies。


为什么不把 react 16 作为一个 dep 放在 foo 里面呢?这样 15 和 16 都将可用并且 foo 可以使用 16 而 mypackage 可以使用 15?
React 是一个在运行时引导的框架,为了让 React 15 和 React 16 存在于同一个页面上,你需要同时引导两者,这对于最终用户来说是非常繁重和有问题的。如果 foo 同时适用于 React 15 和 React 16,那么它可以将其 peerDependency 列为 >=15 < 17
nitinsh99 我的回答是用最简单的例子来解释 peerDependencies 的目的,而不是如何摆脱 peerDependencies 抛出的错误
@nitinsh99 在包依赖项中添加 react 将提供像 Hooks - Multiple reacts in a package 这样的问题