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
相关产品推荐
相关产品推荐

