单仓依赖不同版本react引用错位
背景
我们有一个单仓项目,是基于pnpm workspace构建的,在之前全仓库的子项目,react版本都是使用的17.0.1,
在根目录的package.json中安装了依赖17.0.1,子项目都没有明确的指定react版本,也就是说使用了幻影依赖,一切安好,
直到某一天其中一个项目A将依赖升级到了react18,为了避免影响,所有子项目都在自己的package.json写明了react版本的依赖为17,
然后移除了根目录package.json中的react依赖,等到数天后,其他依赖react17的项目因为更新依赖包,重新 pnpm i
之后,
就出现了错误的引用。
现象
除了react依赖为18的子项目正常,其他所有的依赖为17的子项目启动后,html显示报错,报错的信息如下:
项目里面明明依赖的react是17,为什么会出现react18的引用,并且这个错误就是react和react-dom版本对不上会出现。 说明问题确实出现在react版本上了,然后经过漫长的排查过程。
排查过程
怀疑依赖安装有问题
首先删除所有的node_modules,重新安装依赖,问题并没有得到解决,还是报错。
查看根目录node_modules/.pnpm
能找到两个版本的依赖:
node_modules
|-- .pnpm
| |-- react@17.0.1
| |-- react@18.3.1
查看报错项目的node_modules/react
经过查看,react正常,查看package.json版本也是我需要的17.0.1
{
"name": "react",
"description": "React is a JavaScript library for building user interfaces.",
"keywords": [
"react"
],
"version": "17.0.1",
"homepage": "https://reactjs.org/",
"bugs": "https://github.com/facebook/react/issues",
"license": "MIT",
...
}
怀疑是依赖提升导致
为了验证是不是依赖提升或者根目录有react依赖,导致解析错误,我在根目录中创建了一个测试js
const React = require('react');
console.log(React.version);
执行这个文件后报错:
node:internal/modules/cjs/loader:1031
throw err;
^
Error: Cannot find module 'react'
Require stack:
....
可以证明根目录确实没有react的依赖了。
怀疑pnpm软链接和硬链接错误
通过在react17的项目中查看软链接:
ls -l apps/项目x/node_modules/react
得到结果
lrwxrwxrwx 1 xxxxx users 59 Mar 8 00:04 apps/项目x/node_modules/react -> ../../../node_modules/.pnpm/react@17.0.1/node_modules/react
软链接没有问题,然后查看根目录node_modules/.pnpm目录下的依赖硬链接
ls -i | grep react@18
ls -i | grep react@17
得到结果:
3539278 react@18.3.1
3539319 react@17.0.1
链接也没有问题。
怀疑是node或者打包解析有问题
我手动把node_modules/.pnpm/react@18.3.1
的文件名改成了 node_modules/.pnpm/react@18.3.11
,然后重新启动react17依赖的项目,然后就不报错了。印证了我推测是打包解析或者node在解析依赖的一些默认行为使用了高版本。
调试cra打包的脚本
修改webpack配置,在alias中添加指定别名
alias: {
'react': path.resolve(paths.appPath,'node_modules/react'),
'react-dom': path.resolve(paths.appPath,'node_modules/react-dom'),
},
其中path.resolve(paths.appPath,‘node_modules/react’)就是依赖react17的子项目的 node_modules/react的路径。再次启动后,问题解决。
原因推测
严格来说,我的子项目已经在package.json中已经指定了react@17.0.1,并且我在根目录的pnpm-lock.json中查看这这个应用锁定的依赖版本
apps/项目X:
specifiers:
'@cli/cra': 'workspace: *'
'@dev-ops/mock-data': 'workspce: *'
'@dev-ops/test-utils': 'workspace: *'
'@wangeditor/editor': ^5.1.0
'@wangeditor/editor-for-react': ^1.0.6
copy-to-clipboard: ^3.3.3
query-string: ^7.0.1
react: 17.0.1
react-dom: 17.0.1
react-final-form-hooks: ^2.0.2
tdesign-icons-react: ^0.4.2
tdesign-react: 1.5.5
use-query-params: ^1.2.3
dependencies:
'@wangeditor/editor': 5.1.23
'@wangeditor/editor-for-react': 1.0.6_tyo65jpvqz55vkumg7eqcnajdy
copy-to-clipboard: 3.3.3
query-string: 7.1.3
react: 17.0.1
react-dom: 17.0.1_react@17.0.1
react-final-form-hooks: 2.0.2_react@17.0.1
tdesign-icons-react: 0.4.3_w7o5yyljkiidx2s2nzb26ottzu
tdesign-react: 1.5.5_w7o5yyljkiidx2s2nzb26ottzu
use-query-params: 1.2.3_6lqhnopb2vek3raybzfcsihuai
devDependencies:
'@cli/cra': link:../../cli/cra
'@dev-ops/mock-data': link:../../dev-ops/mock-data
'@dev-ops/test-utils': link:../../dev-ops/test-utils
锁定的依赖版本也没有问题,就算我不在webpack配置中指定alias也不会出现引用到react18的情况,然后我手动指定alias为当前目录的node_modules下的react, 就做了一件画蛇添足的事儿,它就好了,这非常不正常。
虽然到这里找到了解决这个问题的方法,但是问题真实的原因仍然还没有找到,推测有2种可能
- node或者cra默认解析依赖的时候引用错误
- 我们的子项目有某些包有幻影依赖,幻影依赖中有react18的依赖,导致编译的时候引用错误
这两个推测最终原因,仅仅只是推测,单看这2个原因感觉都不会出现,但是几乎可以肯定的是,这个错误出现的原因是, pnpm单仓、多版本依赖,复杂的依赖结构导致了这个错误的出现。