NodeJS 项目中的 PeerDependencies 是什么?
PeerDependencies,当我在终端中收到如下所示的 PeerDependency 警告时,这个术语至少对我来说是令人困惑的:
最近,一个 Node 包中发生了一起恶意代码攻击事件,该攻击大量涉及 PeerDependencies 的概念,这最终激起了我对这个主题的好奇,促使我开始深入研究 PeerDependencies 的工作原理。在这篇博文中,我将记录我关于 NodeJS PeerDependencies 的一些发现,希望能帮助您更好地理解这个主题。
"What are peer dependencies"
当然,用谷歌搜索确实能找到一些结果。然而,谷歌返回的主要参考资料中,没有一个能让我以满意的方式理解 PeerDependencies。一段时间后,我找到了一个Stackoverflow 页面,其中包含Stijn De Witt对 PeerDependencies 的精彩解释。他的解释非常接近于让我理解 PeerDependencies 基础知识,并让我在脑海中产生一些“啊哈!”的瞬间(谢谢 Stijn!)。但不知何故,由于我更倾向于视觉学习,Stijn 的“文本驱动” Stackoverflow 解释并没有让我在理解 PeerDependencies 方面获得那种“最后一英里”的满足感。因此,我根据他的解释绘制了一些代码(您可以在下面看到引用),突然间,我的想法变得清晰起来。
有什么问题?
先声明一下:在接下来的示例中,JillsModule
这将是整个流程中最棘手的部分(随后是 PeerDependency)。因此,我在使用时添加了虚构的版本号附加(@1.0、@2.0)。
假设我们正在构建
OurCoolProject
并使用JacksModule
和JillsModule@2.0
。同时假设JacksModule
也依赖于 JillsModule,但依赖于不同的版本,比如JillsModule@1.0
。只要这两个版本不相容,就不会有问题。 的JacksModule
使用JillsModule
仅仅是一个实现细节。我们进行了JillsModule
两次打包(因为代码使用了两个不同的版本,但彼此之间没有关联!),但当我们获得稳定的开箱即用软件时,这只是一个小小的代价。
在代码中这意味着
// OurCoolProcject.js
import JacksModule from 'jacksmodule';
import JillsModule(@2.0) from 'jillsmodule(@2.0)';
const OurCoolProcject = () => {
// do some stuff with JacksModule
// do some stuff with JillsModule(@2.0). stuff won't break as we have the compatible @2.0 version of JillsModule available in this scope.
}
export default OurCoolProject;
// jacksmodule.js (an npm module)
import JillsModule(@1.0) from 'jillsmodule(@1.0)';
const JacksModule = () => {
// do some stuff with JillsModule(@1.0). stuff won't break as we have the compatible @1.0 version of JillsModule available in this scope.
}
export default JacksModule;
但接下来这种依赖关系会变得更加棘手。
现在假设以某种方式
JacksModule
暴露了它的依赖关系JillsModule
。object instanceof JillsClass
例如,它接受一个……如果我们创建一个new JillsClass
使用该库 2.0 版本的 ,并将其传递给jacksFunction
(我们知道,这不包括 1.0 版本!),会发生什么?一切都会乱套!像 这样的简单事情jillsObject instanceof JillsClass
会突然返回false
,因为jillsObject
实际上是另一个 的实例JillsClass
,也就是 2.0 版本。
在代码中这意味着这样:
// OurCoolProcject.js
import jacksFunction from 'jacksmodule';
import JillsModule(@2.0) from 'jillsmodule(@2.0)'; // node resolves to OUR dependency of JillsModule which is 2.0!
const OurCoolProcject = () => {
const jillsObject = new JillsModule(@2.0).JillsClass;
// next the beginning of all evil, we'll pass a jillsObject of version 2.0
// to jacksFunction (that would expect jillsObject of version 1.0 🤦♀️)
jacksFunction(jillsObject);
}
export default OurCoolProject;
// jacksmodule.js (an npm module)
import JillsModule(@1.0) from 'jillsmodule(@1.0)';
const jacksFunction = (jillsObject) => {
// make sure jillsObject is compatible for further usage in this function
const jillsObjectRocks = jillsObject instanceOf JillsModule(@1.0).JillsClass;
// └─> 🔥🔥🔥 `jillsObjectRocks` will be a big, fat FALSE
// as the JillsModule dependencies actively used in this function and
// passed to this function differ in versions (1.0 vs. 2.0) 🤦♀️
...
}
export default jacksFunction;
你注意到这里发生了什么吗?jacksFunction
收到一个不兼容的错误jillsObject
,因为该对象是基于 JillsModule(2.0) 构建的,而不是基于 JillsModule(1.0)JacksModule
构建的。到目前为止,这仅仅表明了在最坏的情况下会导致软件无法正常工作的问题。
PeerDependencies 如何解决这个问题
幸运的是,npm 内置了一些智能机制来解决这个问题。如果 JacksModule 将 JillsModule(@1.0) 声明为 PeerDependency,npm 会在安装项目依赖项时向用户发出警告。因此,JacksModulepackage.json
应该包含以下声明:
{
"name": "JacksModule",
...
"peerDependencies": {
"JillsModule": "1.x"
},
...
}
因此,npm 的 PeerDepenedency 智能基本上会触发控制台输出,向我们的开发人员发出警告,内容如下:
嘿,我是 JacksModule。我来告诉你:我需要这个特定的 JillsModule 包,但我真正需要的是属于我的 JacksModule 项目并列在我的 package.json 文件中的版本。所以请确保它已安装,并且不是你可能在应用程序的其他地方安装的、供自己使用的 JillsModule 的其他版本。
所以,最终——进一步思考——依赖需要 PeerDependencies 的 npm 包可能会很棘手。假设你需要一个新版本的包 X 供应用程序中单独使用,而你应用程序中使用的另一个依赖项对另一个版本的包 X 也具有 PeerDependency,那么这可能会引发问题。如果出现这种情况——在最坏的情况下还会导致软件出现问题——你就得自己决定使用哪个包,或者哪些代码可能需要重构才能满足所有需求。
我希望这些解释和代码示例能够帮助你理解,并弥补你之前关于 PeerDependencies 的思考空白。如果你有任何疑问或想提出一些文章优化建议,请随时联系我或留言。
这篇文章最初发布于此处。
鏂囩珷鏉ユ簮锛�https://dev.to/btdev/what-are-those-peerdepenedcies-in-a-nodejs-project-51jo