You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React 18跨应用渲染异常:调用renderApp报(0, o.jsx)不是函数

解决React 18跨应用渲染的(0, o.jsx) is not a function错误

问题本质

App2编译后的JS依赖自身打包的React/ReactDOM运行时,但你把它的JS插入到App1页面后,App1已加载的React版本会和App2的React运行时发生全局变量冲突,或者App2的JS没正确初始化自己的模块作用域,导致JSX编译后的o.jsx找不到正确实现。

可行解决方案

1. 修改App2打包配置,复用页面全局的React/ReactDOM

如果用Vite,在vite.config.js中添加:

export default {
  build: {
    rollupOptions: {
      // 不把React/ReactDOM打包进App2的JS
      external: ['react', 'react-dom'],
      output: {
        // 指定全局变量名,对应页面上的React/ReactDOM
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM'
        }
      }
    }
  }
}

如果用Create React App,需要借助react-app-rewired修改webpack配置,新建config-overrides.js:

module.exports = function override(config, env) {
  config.externals = {
    'react': 'React',
    'react-dom': 'ReactDOM'
  };
  return config;
};

这样App2的JS会直接使用页面已存在的React环境,避免版本冲突。

2. 给App2的全局函数绑定正确作用域

修改App2入口文件里的renderApp定义,确保它能访问到自身模块内的React/ReactDOM:

// App2入口文件
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

window.renderApp = (version) => {
  const root = ReactDOM.createRoot(document.getElementById('app2-root'));
  root.render(<App version={version} />);
};

3. 用iframe彻底隔离两个应用(最稳妥)

如果不想让两个应用共享React版本,用iframe完全隔离运行环境,轻松实现多版本渲染:

// App1中加载App2的代码
async function loadApp2(version) {
  const res = await fetch('/app2.html');
  const html = await res.text();
  
  const iframe = document.createElement('iframe');
  iframe.style.width = '100%';
  iframe.style.height = '100vh';
  iframe.style.border = 'none';
  document.body.appendChild(iframe);
  
  // 将App2的HTML写入iframe
  const doc = iframe.contentDocument || iframe.contentWindow.document;
  doc.open();
  doc.write(html);
  doc.close();
  
  // 等待iframe加载完成后调用renderApp
  iframe.onload = () => {
    iframe.contentWindow.renderApp(version);
  };
}

这种方式下两个应用完全独立,不会有任何运行时冲突,适合多版本共存场景。

4. 确保资源加载顺序正确

调用renderApp之前,必须等App2的所有JS、CSS资源加载完成:

// App1中加载资源的示例
async function loadApp2Resources() {
  const res = await fetch('/app2.html');
  const html = await res.text();
  
  // 提取HTML中的JS和CSS链接
  const jsTags = html.match(/<script src="([^"]+)"><\/script>/g) || [];
  const cssTags = html.match(/<link rel="stylesheet" href="([^"]+)"/g) || [];
  
  // 先加载CSS
  cssTags.forEach(cssTag => {
    const href = cssTag.match(/href="([^"]+)"/)[1];
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = href;
    document.head.appendChild(link);
  });
  
  // 加载所有JS并等待全部完成
  const loadPromises = jsTags.map(jsTag => {
    const src = jsTag.match(/src="([^"]+)"/)[1];
    return new Promise(resolve => {
      const script = document.createElement('script');
      script.src = src;
      script.onload = resolve;
      document.body.appendChild(script);
    });
  });
  
  await Promise.all(loadPromises);
  
  // 此时调用renderApp不会报错
  window.renderApp('v1');
}

重要提醒

  • 同一个页面尽量避免加载多个版本的React,否则极易出现hooks失效、JSX函数找不到这类问题。
  • 如果必须使用不同版本的React,iframe是最可靠的隔离方案。
  • 将React设为外部依赖,既能减小App2的打包体积,又能避免版本冲突。

内容的提问来源于stack exchange,提问作者xxcheckmatexx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 16:27:42