为何删除package-lock.json和node_modules可解决React升级peer依赖冲突
问题背景
新加入项目后负责将React从17版本升级至18版本,执行npm install react@latest react-dom@latest升级react、react-dom及部分关联依赖后,出现若干依赖相关警告。多数警告可通过升级对应依赖包直接解决,但遇到了如下无法直接处理的警告:
npm WARN Conflicting peer dependency: react@17.0.2 npm WARN node_modules/react npm WARN peer react@"^16.3.0 || ^17.0.0" from react-side-effect@2.1.1 npm WARN node_modules/react-helmet/node_modules/react-side-effect npm WARN react-side-effect@"^2.1.0" from react-helmet@6.1.0 npm WARN node_modules/react-helmet
排查后发现,存在一个第三方自定义包将react@^17.0.2、react-dom@^17.0.2声明为普通依赖,导致依赖树中存在React 17.0.2版本的安装记录,对应声明如下:
依赖React 17.0.2的自定义包声明
"<CUSTOM PACKAGE MADE BY SOMEONE ELSE>": { "version": "0.3.0", "resolved": <内部包资源地址>, "integrity": "sha1-Ah72HLxApcdcSPGRIE/L7wjy8Ec=", "dependencies": { <CUSTOM PACKAGE MADE THE SAME PERSON 2>: "^0.1.6", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0" } },
实际安装的React 17.0.2依赖记录
"react": { "version": "17.0.2", "resolved": <npm公共包资源地址>, "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" }
附加疑问
- package-lock.json中同时存在两个版本的React是否会引发运行问题?
- 这类场景下React本应被声明为peer依赖的判断是否正确?
排查过程
最初以为需要等待react-helmet适配React 18才能完成升级,但同事反馈其他同样使用react-helmet的项目已经成功升级至React 18,未出现同类警告。对照实验结果如下:
- 仅删除package-lock.json后执行
npm install,仍会出现相同警告 - 同时删除package-lock.json和node_modules目录后重新执行安装,警告完全消失
核心疑问:要求React 17.0.2的依赖包本身并未做任何修改,为何依赖冲突会被自动解决?
备注
已知react-helmet目前已停止维护,后续会替换为其他方案,该点不在本次问题的讨论范围内。
问题解答
关于peer依赖声明的判断
你的判断完全正确。所有面向React生态开发的组件、插件类包,都必须将react、react-dom声明为peerDependencies,而非普通dependencies。这类包本身不应该内置独立的React副本,必须复用宿主项目安装的React版本,否则必然会出现多副本React的问题,你遇到的这个第三方自定义包属于错误的依赖声明写法。
关于多版本React共存的运行风险
一定会引发运行问题。React内部依赖全局状态实现调度、上下文共享机制,同一应用运行两个React副本时,会出现Hooks调用报错(典型如Invalid hook call)、上下文丢失、事件调度异常等问题,且这类问题隐蔽性强,排查成本极高。
关于删除node_modules后警告消失的原因
这是npm v7+版本依赖提升(Hoist)机制的正常表现:
- 仅删除package-lock.json时,node_modules目录下之前安装的嵌套React 17副本不会被清理,npm重新生成依赖树时会优先复用已存在的嵌套包,react-helmet所在的依赖链还是会匹配到这个17版本的React,因此会持续触发peer依赖警告。
- 同时删除package-lock.json和node_modules目录后,npm会从零构建依赖树,优先将符合版本范围的包向顶层提升。此时项目顶层已经安装React 18,npm会判断react-side-effect声明的peer依赖范围
^16.3.0 || ^17.0.0虽然未明确包含18,但实际运行兼容(react-helmet不依赖React 18的专属API,在18版本下可正常运行),因此不会额外安装React 17副本,直接将顶层的React 18共享给所有依赖链使用,警告自然消失。
注意:这种警告消失只是npm安装过程中未触发严格peer校验的结果,并非真正解决了依赖范围不匹配的问题。如果npm开启了严格peer模式(npm v7+默认开启),后续安装新依赖时很可能复现该警告。根治方案有两种:一是联系自定义包的维护者,将
react、react-dom从普通dependencies移至peerDependencies;二是在项目package.json中添加overrides字段,强制所有依赖链复用项目顶层安装的React版本。
内容的提问来源于stack exchange,提问作者Fluous

