React应用中如何从TypeScript调用嵌入HTML页面的JavaScript函数?
问题描述
基于Create React App开发React应用,现有一个基于webkit的HTML Viewer,该Viewer展示的HTML页面中嵌入了全局JavaScript函数sendMessage。需要在React的TypeScript按钮点击时,调用这个HTML页面中的sendMessage函数,此前尝试相关方案未成功。
相关代码片段
HTML页面中的全局sendMessage函数
function sendMessage(str) { theRenderArea.sendMessage(str); }
注:theRenderArea是另一个JS脚本中定义的对象,此函数仅作为中转调用其内部的sendMessage方法。
HTML页面嵌入的theRenderArea.sendMessage实现
this.sendMessage = function(b) { if (this.isConnected()) { b = { type: "command", message: b.toString() }; var a = new h; this.websocket.send(a.encode(b)) } };
TypeScript的Viewer组件
function Viewer() { return ( <> <div id="loaderGroup" style={{display: "none"}}><div id="loader"></div><div id="loaderText">Network calibration</div></div> <div id="TheDiv"></div> </> ) } export default Viewer
React中的按钮定义
<Button style={{color: selected ? "#6A89FF": "#5B5C5D"}} onClick={sendMessage("some string")}> Interact </Button>
解决步骤
1. 修正按钮的onClick绑定逻辑
原按钮代码中onClick={sendMessage("some string")}会在组件渲染时立即执行函数,而非点击时触发,需改为箭头函数包裹:
<Button style={{color: selected ? "#6A89FF": "#5B5C5D"}} onClick={() => window.sendMessage("some string")}> Interact </Button>
2. 解决TypeScript类型报错
由于TypeScript无法识别全局的sendMessage函数,需在项目中声明全局类型:
declare global { interface Window { sendMessage: (str: string) => void; } } // 可将这段代码放在项目根目录的`global.d.ts`文件中,或当前组件文件顶部
3. 确保HTML页面已加载完成
只有当HTML页面完全加载到Viewer中后,sendMessage函数才会存在于全局上下文,需根据Viewer的实现方式做对应处理:
- iframe嵌入场景:给iframe添加加载完成监听,确认加载后再允许调用函数
import { useRef, useEffect, useState } from 'react'; function Viewer() { const iframeRef = useRef<HTMLIFrameElement>(null); const [isHtmlLoaded, setIsHtmlLoaded] = useState(false); useEffect(() => { if (iframeRef.current) { iframeRef.current.onload = () => setIsHtmlLoaded(true); } }, []); const handleSend = () => { if (isHtmlLoaded) window.sendMessage("some string"); }; return ( <> <div id="loaderGroup" style={{display: "none"}}><div id="loader"></div><div id="loaderText">Network calibration</div></div> <div id="TheDiv"> <iframe ref={iframeRef} src="你的HTML页面路径" /> </div> <Button style={{color: selected ? "#6A89FF": "#5B5C5D"}} onClick={handleSend} disabled={!isHtmlLoaded}> Interact </Button> </> ) } - 独立webkit WebView场景(如Electron):需通过WebView的API执行脚本,而非直接访问
windowimport { useRef } from 'react'; function Viewer() { const webviewRef = useRef<Electron.WebviewTag>(null); const handleSend = () => { webviewRef.current?.executeJavaScript(`sendMessage("some string")`); }; return ( <> <div id="loaderGroup" style={{display: "none"}}><div id="loader"></div><div id="loaderText">Network calibration</div></div> <div id="TheDiv"> <webview ref={webviewRef} src="你的HTML页面路径" /> </div> <Button style={{color: selected ? "#6A89FF": "#5B5C5D"}} onClick={handleSend}> Interact </Button> </> ) }
4. 排查上下文隔离问题
如果上述方法仍不生效,需确认webkit Viewer是否开启了上下文隔离。若开启隔离,React的window和HTML页面的window属于不同上下文,无法直接访问,此时需要通过Viewer提供的通信API(如消息传递)来触发sendMessage函数。
内容的提问来源于stack exchange,提问作者cwietholt
相关产品推荐
相关产品推荐

