electron webview通信的桥梁
preload.js (https://www.electronjs.org/zh/docs/latest/api/webview-tag#preload)
//!! 重要文件 误删除 // eslint-disable-next-line @typescript-eslint/no-var-requires const { contextBridge, ipcRenderer } = require('electron') // electron 19.0.0版本 // eslint-disable-next-line @typescript-eslint/no-var-requires const { ypTalkBridge } = require('@yp_cloud/common') // 桥接 contextBridge.exposeInMainWorld( '_ypTalkApi', { ...ypTalkBridge(ipcRenderer), isMainFrame: process.isMainFrame } )
ypTalkBridge.js
/* * @Date: 2022-07-12 15:57:39 * @Description: 此文件不需要改动什么东西 */ import { apiData } from '../config'
// apiData = [ // 选择人员 {name: 'choose', 'channel': 'choose'}]
interface IpcRequestListenerParams { onSuccess: (v?: any) => void onFail: (v?: any) => void } let ipcRenderer = null let msgKey = 0 // 消息标识 let requestListenerMap: Map<string, IpcRequestListenerParams> = new Map() // 事件订阅 let requestPromiseMap: Map<number, Function> = new Map() // 回调中心 /** * 添加ipc请求回报 * @enerKey 当前通信的标识key * @channelName 回报时的 标识 channel * @params 回调时的参数 */ const addIpcRequestListener = (enerKey: string, channelName: string, params: IpcRequestListenerParams) => { if (!requestListenerMap.has(enerKey)) { // 回复信息时 ipcRenderer.on(channelName, (_event: any, params) => { const {msgno, data, eventName} = params // 找出事件中心是否存在 if (msgno && requestPromiseMap.has(msgno)) { const resolve = requestPromiseMap.get(msgno) // 如果eventName存在,代表是params方法参数的回调 const eventListener = requestListenerMap.get(enerKey) if (eventName) { // 触发回调 eventListener?.[eventName]?.(data) resolve(data) } else { resolve(data) } // 删除回调 requestPromiseMap.delete(msgno) } }) // 添加到事件中心 requestListenerMap.set(enerKey, params) } } /** * 发送请求 * @ipcKey 通信的key * @params 参数 */ const ipcRequest = (ipcKey: string, params?: any) => { return new Promise(resolve => { // 防止sdk,事件名和渲染进程重复,这里对传递进来的事件名重新定义 const channel = 'talk-' + ipcKey // 明确告知渲染层回信时的key,通过channel发送消息 const eventName = channel + '-reply' // 添加唯一辨识 const msgno = ++msgKey // 添加回调 requestPromiseMap.set(msgno, resolve) // 添加事件 addIpcRequestListener(channel, eventName, { onSuccess: params?.onSuccess, onFail: params?.onFail }) // 过滤数据,发送 Functions, Promises, Symbols, WeakMaps, 或 WeakSets 将抛出异常 const data = params ? JSON.parse(JSON.stringify(params)) : null // 发送消息给渲染进程webview ipcRenderer.sendToHost(channel, { eventName, msgno, data }) }) } // sdk暴露给h5的方法 const ypTalkBridge = (copyIpcRenderer) => { const api = {} ipcRenderer = copyIpcRenderer || null apiData.forEach(item => { api[item.name] = (params) => ipcRequest(item.channel, params) }) return api } export default ypTalkBridge
渲染层的webview:
// 接受webview页面通信 webview.addEventListener('ipc-message', (event) => { if (event.channel == 'talk-choose') { // 吊起人员选择 permissionDialog({ onConfirm: (data) => { const params = event.args[0] webview.send(params?.eventName, { msgno: params?.msgno, data, eventName: 'onSuccess' }) } }) } })
ypTalkApi.js
interface Ichoose { name: string, onSuccess?: (v: any) => void } const tip = '请在桌面端调用此方法' const {_ypTalkApi } = window as any // 是否在桌面端运行 const isYpTalk: boolean = _ypTalkApi?.isMainFrame || false // 选择人员 const choose = (params: Ichoose) => { return new Promise(async (resolve, reject) => { if (!isYpTalk) { reject(tip) } else { const data = await _ypTalkApi?.choose(params) resolve(data) } }) } export default { isYpTalk, choose }
web网页 嵌入的页面:
// electron通讯方法(向客户端发送消息) const sendToElectronPage = async () => { ypTalkApi.choose({ name: '哈哈哈' }).then(data => { console.log(data) }) } useEffect(() => { sendToElectronPage() }, [])