peerDependencies是什么
package.json 是 Node.js 和前端开发中的重要组成部分,其中包含了项目的各种配置信息,包括名称、版本、脚本、依赖关系等。而在依赖管理中,dependencies、devDependencies、以及 peerDependencies 各自扮演着不同的角色。
什么是 peerDependencies
peerDependencies 是 Node.js 生态系统中的一个关键概念,用来描述软件包之间的一种特定的依赖关系。简单来说,peerDependencies 用于描述项目对某些依赖的共存期望,尤其是在插件或库的开发中。
在 package.json 中,peerDependencies 的主要作用是表示项目需要一个特定版本的依赖项,但并不主动去安装它,而是将安装这个依赖的责任交给最终的使用者。换句话说,peerDependencies 描述了一种对环境的需求,要求环境中存在某个特定版本的包,而不是直接把它安装在当前模块中。
假设你有一个插件 A,它需要在某个主项目(例如 Angular 项目)中运行,并且插件 A 需要使用与该主项目版本兼容的某个包(比如 RxJS)。在这种情况下,插件 A 不会主动安装 RxJS,而是期待最终的用户项目中已经安装了与插件兼容的版本。这个需求就可以通过 peerDependencies 来表达。
peerDependencies 的设计目的
peerDependencies 的最初设计是为了处理那些必须由多个组件共享的依赖关系,以避免重复依赖和版本冲突。这在插件、工具类库的场景中尤为重要。例如,当多个库或插件依赖同一个主应用的同一个版本时,保持依赖一致性就显得至关重要。
假设有一个 Angular 项目,而你正在开发一个需要依赖 Angular 的插件,这时你希望插件使用和项目相同的 Angular 版本。这就意味着你不能简单地在插件中声明 dependencies 并安装一个特定版本,因为这可能会导致项目中有多个不同版本的 Angular 共同存在,进而引发不可预测的问题。peerDependencies 的引入就是为了在这种情况下,让插件依赖项目已经安装的 Angular 版本,而不是自己安装一个版本。
通过 peerDependencies,开发人员可以确保库或插件使用的依赖与宿主环境一致,这对于插件和框架之间的版本兼容性至关重要。
peerDependencies 与 dependencies 的对比
为了更好地理解 peerDependencies,我们可以对比它与 dependencies 和 devDependencies 的不同之处。
dependencies:这个字段用于声明项目的必需依赖,当我们安装一个项目时,这些依赖会被自动安装到node_modules文件夹中。例如,一个 Angular 应用可能将@angular/core作为dependencies,因为它是应用正常运行所必需的部分。devDependencies:这个字段用于声明开发时的依赖,例如构建工具、测试框架等。这些依赖通常在开发环境中使用,但在生产环境中不需要。例如,一个项目的typescript编译器通常是devDependencies,因为它只在开发和构建过程中需要。peerDependencies:与上面两个不同,它并不直接安装依赖,而是用来说明:你的项目需要某个依赖,并且这个依赖的版本需要与宿主项目中的其他依赖一致。它的作用是告诉宿主项目,当前模块需要的依赖应该由宿主项目来提供,从而避免重复依赖和潜在的冲突。
具体来说,如果在某个库的 package.json 中定义了 peerDependencies,而最终用户在安装这个库时没有满足这个依赖条件,npm 或 yarn 通常会给出一个警告,提示用户去安装正确的依赖版本。
peerDependencies 的适用场景
peerDependencies 通常适用于以下几种场景:
1. 插件与宿主的依赖关系
peerDependencies 最常见的使用场景是当某个包是宿主应用的插件时。插件需要依赖宿主应用的特定版本来正常工作,因此插件需要与宿主应用共享某些库的版本。例如,Angular、React 等框架的插件和库会将框架本身定义为 peerDependencies,确保这些插件和库与框架的版本一致。
举例来说,假设你在开发一个 Angular 的插件 my-angular-plugin,并希望它兼容 Angular 版本 13。那么你可以在 package.json 中这样定义:
{
"name": "my-angular-plugin",
"version": "1.0.0",
"peerDependencies": {
"@angular/core": "^13.0.0"
}
}
这样,当用户在 Angular 13 的项目中安装这个插件时,插件将使用项目中已经安装的 Angular,而不会安装自己的版本。
2. 多个插件共享同一依赖
另一个常见的场景是当多个插件需要共享同一个依赖时。例如,React 项目中有很多第三方库需要使用同样的 React 版本。如果这些库各自安装不同版本的 React,就会产生多个 React 实例,这可能导致严重的问题,例如 React 的 hook 功能异常等。因此这些库通常会将 React 定义为 peerDependencies,从而确保它们使用的都是项目中已安装的版本。
my-react-component-library,你希望它使用项目中现有的 React 版本,这样可以避免出现多个不同版本的 React。你可以在 package.json 中这样定义: {
"name": "my-react-component-library",
"version": "1.0.0",
"peerDependencies": {
"react": "^17.0.0"
}
}
3. 避免重复安装
当不同的库或者插件依赖同一个模块的不同版本时,可能会导致重复安装。例如,多个插件都依赖于 RxJS 库,但版本不同,最终会在 node_modules 中安装多个版本的 RxJS,进而造成冲突。通过将 RxJS 定义为 peerDependencies,可以确保各个插件和项目本身使用相同的 RxJS 版本,避免版本冲突和重复安装的问题。
使用 peerDependencies 的合理场景
- 框架与插件场景:如果你在开发的库是作为某个框架的插件,那么你应该考虑将这个框架作为
peerDependencies。这样可以确保你的插件和主框架的版本一致,而不会导致不同版本间的兼容性问题。 - 工具库的版本同步:当你开发的库与其他工具库有紧密的版本联系时,使用
peerDependencies可以确保这些工具库之间的版本协调一致。例如,一个对 Lodash 版本有特定依赖的库,可以将 Lodash 作为peerDependencies,确保使用者安装时能够满足正确的版本要求。 - 避免模块实例的重复:在某些情况下,一个依赖被重复安装会导致多次实例化,进而引发功能性错误。例如 React 的 Hooks 在多个 React 实例中不共享状态,可能会导致状态管理不正确的问题。因此,把 React 作为
peerDependencies可以避免这个问题。
安装 peerDependencies
peerDependencies 不会像 dependencies 那样自动安装,而是会给出警告,让用户手动安装所需的依赖。
在 npm 3 版本之后,peerDependencies 发生了一些重要变化:它不再自动安装这些依赖,而是要求使用者显式地安装对应版本的依赖。
举个例子,如果你的 package.json 中包含了如下定义:
{
"peerDependencies": {
"react": "^17.0.0"
}
}
当用户尝试安装这个库时,npm 会提示用户需要安装 react@^17.0.0,以确保版本兼容。这样,用户可以自由选择与项目其他部分相兼容的版本。
如何应对 peerDependencies 的警告
当用户安装一个有 peerDependencies 的库时,npm 会给出警告,提示用户需要安装的依赖。这些警告有助于确保安装环境满足要求,但有时也可能造成困扰。用户在看到这些警告时,需要按提示安装所需的依赖。
例如,如果你尝试安装某个库 library-a,并收到如下警告:
library-a@1.0.0 requires a peer of react@^17.0.0 but none is installed. You must install peer dependencies yourself.
这时,用户需要手动安装正确的 React 版本:
npm install react@^17.0.0
这样可以确保依赖关系的正确性,并且避免库的使用过程中出现由于版本不一致引起的问题。
举例说说
helloWorld 工程,已经在其 package.json 的 dependencies 中声明了 packageA,有两个插件 plugin1 和 plugin2 也依赖 packageA,如果在插件中使用 dependencies 而不是 peerDependencies 来声明 packageA,那么 npm install 安装完 plugin1 和 plugin2 之后的依赖图是这样的:(前提是依赖的 packageA 的版本都不同)├── helloWorld
│ └── node_modules
│ ├── packageA
│ ├── plugin1
│ │ └── nodule_modules
│ │ └── packageA
│ └── plugin2
│ │ └── nodule_modules
│ │ └── packageA
从上面的依赖图可以看出,helloWorld 本身已经安装了一次packageA,但是因为因为在plugin1 和 plugin2 中的 dependencies 也声明了 packageA,所以最后 packageA 会被安装三次,有两次安装是冗余的。
需要注意的是 npm 现在对于依赖版本相同的包会在 install 的时候自动扁平化(npm v3 之后是这么做的),也就是说,当多个 package 依赖了相同的包且他们的版本也相同的时候,仅仅会在项目的根 node_modules 下安装一份。
而 peerDependency 就可以避免类似的核心依赖库被重复下载的问题。
如果在 plugin1 和 plugin2 的 package.json 中使用 peerDependency 来声明核心依赖库,例如: plugin1/package.json
{
"peerDependencies": {
"packageA": "1.0.1"
}
}
plugin2/package.json
{
"peerDependencies": {
"packageA": "1.0.1"
}
}
在主系统中声明一下 packageA: helloWord/package.json
{
"dependencies": {
"packageA": "1.0.1"
}
}
此时在主系统中执行 $ npm install 生成的依赖图就是这样的:
├── helloWorld
│ └── node_modules
│ ├── packageA
│ ├── plugin1
│ └── plugin2
可以看到这时候生成的依赖图是扁平的,packageA 也只会被安装一次。

浙公网安备 33010602011771号