likes
comments
collection
share

react/jsx-runtime抛错导致项目异常

作者站长头像
站长
· 阅读数 81

背景

目前主要是B端的开发,系统都是微前端模式,会有主子应用之分。需要先加载主应用,然后再加载子应用,由于主应用都是基于React开发的,为了更好的性能,由主应用暴露reactreact-dom这些公共库,然后子应用将这些依赖配置为external,构建时就不会重复打包了。

问题

配置external之后,本地开发时,发现项目竟然有问题:

react/jsx-runtime抛错导致项目异常

TypeError: Cannot read properties of undefined (reading 'getStackAddendum')

出现一个TypeError的异常导致整个子应用都运行不起来。

分析

首先是debug一下有异常的代码,发现缺少 ReactDebugCurrentFrame 属性,导致出现异常。

react/jsx-runtime抛错导致项目异常

把 Webpack中的external配置移除,重新启动项目,项目正常。故大致问题应该是版本原因,external的是一个production版本的react,而本地启动时,因为process.env.NODE_ENVdevelopment

react/jsx-runtime抛错导致项目异常 故加载的了 react-jsx-runtime.development.js 这个文件,导致异常。

修改一下node_modules中的这个文件 node_modules/react/jsx-runtime.js 不区分环境,都使用 cjs/react-jsx-runtime.production.min.js

react/jsx-runtime抛错导致项目异常

然后加上 external 配置,重新启动项目,发现一切正常。 故证实了react的production版本和jsx-runtime.development不匹配会出现问题。

解决方案

知道问题后,我尝试过三个方案,其中有两个是可用的:

方案一:设置NODE_ENV为production (不可用)

看react的实现会根据 process.env.NODE_ENV 判断引用的文件,那我们将这个设置为 production不就可以引用production版本的jsx-runtime了么? 修改这个变量可以通过 # DefinePlugin 实现:

new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production'),
});

这个思路确实没有毛病,但修改之后会引入的新的问题,而且也不好评估影响的访问,故没有继续探索,直接放弃这个方案。

方案二:区分环境,本地开发移除external配置(推荐)

因为是 react 的 production 版本和 jsx-runtime的development不兼容会有问题,那我们如果能将这两个版本统一一下不就好了么。线上使用的肯定是production版本,不会遇到问题,那么我们本地继续使用 development 版本就好了。

具体做法是通过判断 NODE_ENV 如果是本地环境即:

if (process.env.NODE_ENV === 'development') {
   delete external.react;
   delete external['react-dom'];
}

这样可以兼顾本地开发的效率(develop版本会议一些提醒,例如list需要写唯一的key等),和线上的体验。

但有个一悲伤的场景,如果项目使用 Webpack 的module federation加载组件的话,这个解法会有问题,故请看第三种方式。

方案三:乾坤大挪移,替换development版本为production版本 (可用)

方案一想都统一为production版本但确失败了,但还有一个方式可以实现。Webpack在构建过程中会执行不同的plugin,正好有一个官方的plugin可以构建时替换资源路径——# NormalModuleReplacementPlugin

因此可以用这个插件来替换development版本为production版本:

if (process.env.NODE_ENV === 'development') {
   config.plugins?.push(
    new webpack.NormalModuleReplacementPlugin(
      /\/cjs\/react-jsx-runtime.development.js/,
      (resource) => {
        resource.request = resource.request.replace(
          'react-jsx-runtime.development',
          'react-jsx-runtime.production.min'
        );
      }
    )
  );
}

但这种方式缺失了React最佳实践的检测,虽然检测出来东西我们不一定改。

总结

因为一个external引发了各种问题,不同问题会有不同的解法,问题不重要,重要的是知道怎么分析问题。能准确定义问题,其实解决方案就不远了。

转载自:https://juejin.cn/post/7185140351303483450
评论
请登录