Monorepo中不同子包依赖不同React版本导致构建失败怎么办?
在Lerna项目里有两个子包,一个用React18另一个用React17,当我运行lerna run build时老是报错说peer dependency冲突。已经试过用 resolutions强行指定版本,但构建还是失败,控制台全是红色警告:
warning "react-components@1.0.0" has incorrect peer dependency "react@^17.0.2".
warning "new-feature@2.0.0" has incorrect peer dependency "react@^18.2.0".
改成了Yarn Workspaces后虽然安装没问题,但打包时两个包的React版本还是冲突,该怎么优雅地隔离它们的依赖呢?
我的做法是用Yarn Workspaces的nohoist功能,把两个子包的React依赖隔离开。在你的根目录
package.json里加上这个配置:这样每个子包会各自维护自己的React副本,不会提升到根目录。删掉node_modules重新装一遍依赖。
然后还有个关键点,打包工具那边也要配合一下。如果你用的是Webpack,在子包的配置里加上
resolve.alias,强制指定React的路径:这样能确保打包时引用的是当前包里的React版本,而不是莫名其妙跑到别的包里去找。
说实话,如果两个包之间没有依赖关系的话,最省心的办法其实是分开构建。我后来项目里实在被这个折腾得不行,干脆写了个脚本,进到每个子包目录下单独跑npm install和build,虽然土了点但确实稳。
对了,如果你用的是Webpack 5,还可以考虑Module Federation,把React 18的包当成远程模块加载,彻底隔离运行环境。不过这个改动就有点大了,看你的实际情况吧。
首先,确保你的项目已经切换到 Yarn Workspaces 或者 pnpm 的 workspace 模式,因为这些工具对 Monorepo 的支持更好。你提到已经试过 Yarn Workspaces,那就继续用它。
接下来,给每个子包设置独立的 node_modules。具体做法是在每个子包的 package.json 中添加一个字段
"installConfig": { "hoisting": false }。这个配置会告诉 Yarn 不要将这个包的依赖提升到根目录的 node_modules,从而防止不同版本的 React 被注入到同一个上下文中。如果你使用的是 pnpm,它默认就会为每个子包创建独立的 node_modules 结构,所以不需要额外配置。不过要注意,pnpm 的 symlink 机制可能会导致一些特殊的构建工具链问题,记得检查一下你的 Webpack 或 Rollup 配置是否正确处理了 pnpm 的依赖路径。
然后,重点来了,你需要确保构建工具不会错误地解析到错误版本的 React。比如,如果你用的是 Webpack,可以在每个子包的 Webpack 配置里明确指定 resolve.alias,像这样:
这一步是为了强制 Webpack 只使用当前子包下的 React 版本,而不是去根目录或者其他地方找。
另外,提醒一下,peer dependency 的警告虽然烦人,但并不一定会影响运行。如果构建成功了,只是控制台有警告,可以暂时忽略。不过从长期维护的角度来看,最好还是尽量升级所有子包到同一个 React 版本,减少技术债。
最后,还有一个更激进的方案是用工具比如
module-federation或者微前端架构,把两个子包彻底拆成独立的应用,这样它们的依赖完全隔离,但这个方案改动比较大,适合复杂场景。总之,推荐先试试独立 node_modules 加上 Webpack alias 的组合,这是成本最低、见效最快的解法。如果还有问题,可以再细聊。