例如,为什么 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 插件(或其他)冲突?
TL;DR: peerDependencies
用于暴露给消费代码(并预期被消费代码使用)的依赖项,而不是 “私有” 依赖项暴露,并且只是一个实现细节。
对等依赖关系解决的问题
NPM 的模块系统是分层的。更简单场景的一大优势是,当您安装 npm 包时,该包会自带依赖项,因此它可以开箱即用。
但在以下情况下会出现问题:
您的项目和您正在使用的某个模块都依赖于另一个模块。
这三个模块必须相互通信。
在示例中
假设您正在构建 YourCoolProject
,并且同时使用 JacksModule 1.0
和 JillsModule 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 私有版本一起工作。
我建议你先再读一遍这篇文章。这有点令人困惑,但 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 库的最新和最好的临时降级,也不愿根本无法使用它。
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。
foo
同时适用于 React 15 和 React 16,那么它可以将其 peerDependency 列为 >=15 < 17
。
"grunt": "0.4.4"
位于 devDependencies 和 peerDependencies 中,我认为在其中有一个副本确实有意义,因为这意味着我需要该grunt
包供我自己使用,但是我的图书馆的用户也可以使用他们自己的版本,只要它尊重 peerDependencies 版本锁定。那是对的吗?还是 OP 示例是一个非常糟糕的示例?JacksModule
依赖于JillsModule ^1.0.0
,其中JillsModule
是JacksModule
的对等依赖项,并且YourCoolProject
使用JacksModule
和JillsModule ^2.0.0
,我们将收到对等依赖项警告由 NPM 提供,它会建议我们也安装JillsModule ^1.0.0
。但那会发生什么呢?YourCoolProject
现在将有两个版本的JillsModule
可通过import jillsModule from "..."
导入?我怎么记得当我使用JacksModule
时我需要传递一个JillsModule v1.0.0
的实例?JacksModule
存储库,将其升级为依赖于JillsModule ^2.0.0
并向项目维护者提供 PR。首先提交一个错误说明此依赖项已过时并且您想帮助更新它可能会有所帮助。如果你做了一个好的 PR,大多数库维护者会合并它并感谢你。如果维护者没有响应,你可以将你的 fork 发布到以你的名字命名的 NPM 并使用你的 fork。无论如何,有解决方案,但peerDependencies
不能自行解决。