前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >一文搞懂peerDependencies

一文搞懂peerDependencies

作者头像
astonishqft
发布于 2022-05-10 12:32:35
发布于 2022-05-10 12:32:35
89200
代码可运行
举报
运行总次数:0
代码可运行

问题引出

今天在运行之前的一个react工程时,浏览器上抛了一个奇怪的错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

为什么说奇怪呢?是因为这个工程之前是可以运行的,我重新装了一遍依赖之后就不能跑了,真是喜闻乐见了😢。

根据错误提示,这应该是 React Hooks 报的错,通过排除法,确认是我之前封装的一个组件有问题,这个组件是通过npm包安装使用的。只要在项目中去除这个组件,报错就消失了,但是具体出了什么问题,只能祭出 Google 大法了。

果然,有人遇到了跟我一样的问题,在 react 官网找到了此报错的详细说明:

归纳总结一下可能是以下几个原因导致的:

  • React 和 React DOM 版本不匹配
  • 打破了 Hook 的规则 你只能在当 React 渲染函数组件时调用 Hook:
  • ✅ 在函数组件的顶层调用它们。
  • ✅ 在自定义 Hook 的顶层调用它们。
  • 重复的 React

关于第一点,官网解释说有可能使用了不支持 React Hookreact-dom 版本(<16.8.0),这点通过确认 package.json 中的 react-dom 版本号得以排除。

再看第二点,关于 Hooks 用法的问题,因为这段代码以前跑成功过,而且通过检查,也可以很确定的排除了。

最后只剩下第三点,仔细看一下,这一点官网也做了详细的描述:

In order for Hooks to work, the react import from your application code needs to resolve to the same module as the react import from inside the react-dom package. If these react imports resolve to two different exports objects, you will see this warning. This may happen if you accidentally end up with two copies of the react package.

嗯,看到这个描述有点豁然开朗的感觉,为了使 Hook 正常工作,应用代码中的 react 依赖以及 react-dom 的 package 内部使用的 react 依赖,必须解析为同一个模块。 如果这些 react 依赖解析为两个不同的导出对象,你就会看到本警告。这么分析完应该就是我封装的组件中依赖的 react 和 react-dom 的版本号和主工程中所依赖的 react 和 react-dom 的版本号不一致导致的。

找到了导致报错的原因,那么要如何解决呢?

现在想要实现的效果是:

在我开发的 packageA 里面依赖的 reactreact-dom 的版本号应该和主系统中安装的 reactreact-dom 的版本号保持一致,并且 packageA 被安装到主系统中之后,就应该依赖于主系统中的 reactreact-dom

继续看完文档,其中有一句话引起了我的注意:

For example, maybe a library you’re using incorrectly specifies react as a dependency (rather than a peer dependency).

这里提示我说我使用的库可能错误地指定 react 作为 dependency(而不是 peer dependency)。

很好,这里出现了一个 package.json 中的配置项叫做 peerDependencies,正常开发中我们经常接触到 dependenciesdevDependencies, 那么 peerDependencies 又是什么鬼?

初识peerDependencies

peerDependencies 在我们进行一些插件开发的时候会经常用到,比如 jQuery-ui 的开发依赖于 jQueryhtml-webpack-plugin 的开发依赖于 webpack等。

总结一下有如下特点:

  • 插件正确运行的前提是,核心依赖库必须先下载安装,不能脱离核心依赖库而被单独依赖并引用;
  • 插件入口api 的设计必须要符合核心依赖库的规范;
  • 插件的核心逻辑运行在依赖库的调用中;
  • 在项目实践中,同一插件体系下,核心依赖库版本最好是相同的;

举个栗子🌰:

假设现在有一个 helloWorld 工程,已经在其 package.json 的 dependencies 中声明了 packageA,有两个插件 plugin1 和 plugin2 他们也依赖 packageA,如果在插件中使用 dependencies 而不是 peerDependencies 来声明 packageA,那么 $ npm install 安装完 plugin1 和 plugin2 之后的依赖图是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.
├── helloWorld
│   └── node_modules
│       ├── packageA
│       ├── plugin1
│       │   └── nodule_modules
│       │       └── packageA
│       └── plugin2
│       │   └── nodule_modules
│       │       └── packageA

从上面的依赖图可以看出,helloWorld 本身已经安装了一次packageA,但是因为因为在 plugin1 和 plugin2 中的 dependencies 也声明了 packageA,所以最后 packageA 会被安装三次,有两次安装是冗余的。

peerDependency 就可以避免类似的核心依赖库被重复下载的问题。

如果在 plugin1 和 plugin2 的 package.json 中使用 peerDependency 来声明核心依赖库,例如:

plugin1/package.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "peerDependencies": {
    "packageA": "1.0.1"
  }
}

plugin2/package.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "peerDependencies": {
    "packageA": "1.0.1"
  }
}

在主系统中声明一下 packageA:

helloWorld/package.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "dependencies": {
    "packageA": "1.0.1"
  }
}

此时在主系统中执行 $ npm install 生成的依赖图就是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.
├── helloWorld
│   └── node_modules
│       ├── packageA
│       ├── plugin1
│       └── plugin2

可以看到这时候生成的依赖图是扁平的,packageA 也只会被安装一次。

因此我们总结下在插件使用 dependencies 声明依赖库的特点:

  • 如果用户显式依赖了核心库,则可以忽略各插件的 peerDependency 声明;
  • 如果用户没有显式依赖核心库,则按照插件 peerDependencies 中声明的版本将库安装到项目根目录中;
  • 当用户依赖的版本、各插件依赖的版本之间不相互兼容,会报错让用户自行修复;

总结

明白了 peerDependencies 的用法,那么回到开头的问题来,问题就迎刃而解了。

首先在主系统的 package.json 中的 dependencies 声明下 reactreact-dom 的版本:

主系统package.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "dependencies": {
    "react": "^16.13.1",
    "react-dom": "^16.13.1"
  }
}

接着在组件库的 package.json 中的 peerDependencies 声明 reactreact-dom 的版本:

组件的package.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "peerDependencies": {
    "react": ">=16.12.0",
    "react-dom": ">=16.12.0"
  }
}

这样在主系统中执行 $ npm install 之后,主系统和组件库就能共用主系统的 node_module 中安装的 reactreact-dom 了。

到这里,终于可以大声喊出一句 "奥利给!",问题圆满解决ღ( ´・ᴗ・` )比心。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端架构师笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
package.json 配置完全解读
package.json 是前端每个项目都有的 json 文件,位于项目的根目录。许多脚手架在搭建项目时也会自动帮我们自动初始化好 package.json。
Leecason
2022/12/16
2.8K0
package.json 配置完全解读
NPM 组件你应该知道的事
开发一个 npm 组件, 你是否了解需要对外导出什么格式的代码?如何让 npm 组件体积尽可能小?
lucifer210
2020/09/30
1.7K0
NPM 组件你应该知道的事
大佬,第三方组件的Hooks为啥报错了?
http://localhost:8081/Users/项目目录/node_modules/组件库/node_modules/react/cjs/react.development.js
公众号@魔术师卡颂
2021/04/21
2.2K0
npm 依赖管理中被忽略的那些细节
? 这是第 66 篇不掺水的原创,想要了解更多,请戳上方蓝色字体:政采云前端团队 关注我们吧~ 本文首发于政采云前端团队博客:npm 依赖管理中被忽略的那些细节 https://www.zoo.te
政采云前端团队
2020/08/26
2.6K0
npm 依赖管理中被忽略的那些细节
【CodeSandbox】:Sandpack Packager 解析
CodeSandbox 是一个在线的代码编辑器,主要聚焦于创建 Web 应用项目。
WEBJ2EE
2021/02/26
1.9K0
【CodeSandbox】:Sandpack Packager 解析
大家都能看得懂的源码(一)ahooks 整体架构篇
本文是深入浅出 ahooks 源码系列文章的第一篇,这个系列的目标主要有以下几点:
GopalFeng
2022/08/01
7520
大家都能看得懂的源码(一)ahooks 整体架构篇
Webpack 踩坑记 - 配置 externals 和 output
平时用 webpack 可能不会有太大问题,可一旦你开发的包被别人引用的时候,就会存在问题;我最近遇到这么一个场景耗费我很多时间去重新学习 webpack 的打包输出。
JSCON简时空
2020/03/31
3.7K1
Webpack 踩坑记 - 配置 externals 和 output
npm ERR! ERESOLVE unable to resolve dependency tree的解决方法
在 package.json 文件中,存在一个叫做 peerDependencies(对等依赖关系)的对象,它包含了项目里需要的所有的包或用户正在下载的版本号相同的所有的包。
德顺
2022/09/20
3K0
React项目实战(React后台管理系统、TypeScript+React18)-环境准备(1)
这是一套Typescript+React+Antd的通用后台管理系统的视频,我会在这个视频里面带着大家徒手搭了一套基于react的后台管理系统基础设施,之所以叫通用,是因为不管以后做什么类型的管理系统,都可以直接拿这一套代码快速上手项目。
王小婷
2023/11/02
7230
React项目实战(React后台管理系统、TypeScript+React18)-环境准备(1)
npm 详解
💡 概念: npm,全称为Node Package Manager,是随Node.js一起分发的开源包管理系统,也是JavaScript生态中最流行的依赖管理工具。它不仅为Node.js项目提供便捷的包管理服务,还广泛应用于前端、后端甚至跨平台开发领域。
空白诗
2024/06/14
2240
详解从 0 发布 react 组件到 npm 上
之前我发布了我的第一个 npm 组件,一个基于 react 的 3d 标签云组件。在这途中我也是遇到了很多的坑,花在完善整个发布流程的时间远多于写这个组件本身的时间,所以我记录下我觉得一个正常的 react 组件的发布流程
桃翁
2019/03/04
1.6K0
详解从 0 发布 react 组件到 npm 上
React:不要动,否则你会被炒鱿鱼
不知道大家在用React开发时,有没有注意到react与react-dom这两个包中有个很奇葩的属性__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:
公众号@魔术师卡颂
2022/11/22
8610
React:不要动,否则你会被炒鱿鱼
[译] Node 模块中的 peer dependencies 是什么?
原文:https://flaviocopes.com/npm-peer-dependencies/
江米小枣
2020/06/15
1.9K0
Module Federation最佳实践
Module Federation[1]官方称为模块联邦,模块联邦是webpack5支持的一个最新特性,多个独立构建的应用,可以组成一个应用,这些独立的应用不存在依赖关系,可以独立部署,官方称为微前端。
Maic
2022/07/28
1.5K0
Module Federation最佳实践
package.json 知多少?
在 Node.js 中,模块是一个库或框架,也是一个 Node.js 项目。Node.js 项目遵循模块化的架构,当我们创建了一个 Node.js 项目,意味着创建了一个模块,这个模块必须有一个描述文件,即 package.json。它是我们最常见的配置文件,但是它里面的配置你真的有详细了解过吗?配置一个合理的 package.json 文件直接决定着我们项目的质量,本章就带大家了解下 package.json 的各项详细配置。
ConardLi
2019/12/02
1.9K0
你真的了解package.json吗?
大家好,我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder。
用户6256742
2024/05/18
1360
你真的了解package.json吗?
关于前端大管家package.json,你知道多少
今天来看看前端的大管家 package.json 文件相关的配置,充分了解这些配置有助于我们提高开发的效率,规范我们的项目。文章内容较多,建议先收藏在学习!
程序员法医
2022/08/11
1.5K0
关于前端大管家package.json,你知道多少
「React缓存页面」从需求到开源(我是怎么样让产品小姐姐刮目相看的)
最近在开发业务项目的时候,产品小姐姐突然来到我身边,然后就对着电脑一顿操作,具体场景大致是这样的。
用户3806669
2021/04/16
1.9K0
「React缓存页面」从需求到开源(我是怎么样让产品小姐姐刮目相看的)
关于npm 包更新工具npm-check-updates 使用详解
首先说一下包版本的控制 假设 package.json 的包版本如下 "dependencies": { "vue": "^2.5.0", "vuex": "~3.1.0", "vue-router": "3.5.3", "react": "15.4.x", "typescript": "3.x.x", "react-dom": "*.*.*", "react-draggable": "x.x", "classnames": "x", "pinia": "*" } 执行
用户10106350
2022/10/28
1.4K0
关于npm 包更新工具npm-check-updates 使用详解
Vite 学习(三) - rollup & esbuild 基础学习
本小节介绍下 rollup 和 esbuild 的基础用法,我们都知道 vite 本身使用的 rollup 打包,vite 的插件也和 rollup 的插件机制相吻合; esbuild 是用于在开发环境中对文件进行处理,也有自己的声明周期钩子函数,由于对文件的分割和 css 支持还不太友好,暂未应用到打包环节。
测不准
2022/02/13
2.4K0
推荐阅读
相关推荐
package.json 配置完全解读
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文