独书先生 Menu

electron通信 主进程与渲染进程/主进程与webview通信

一.electron主进程和渲染进程通信

进程通信分为异步和同步通信

官网有例子https://electronjs.org/docs/api/ipc-main,稍作解析

异步通信

//主进程中
const { ipcMain } = require('electron')
//主进程接收渲染进程发来的消息,回调中使用event.reply返回消息给渲染进程
ipcMain.on('asynchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.reply('asynchronous-reply', 'pong')
})

//主进程向渲染进程发送消息, win为new BrowserWindow创建的窗口
const msgObj = { type: 'video', options: 'video_01' }
//方法一:
win.webContents.send('main', msgObj)

//方法二:
可以通过id定位到对应的渲染进程发送消息
const { webContents } = require('electron')
webContents.fromId(win.webContents.id).send('main', msgObj)
//-------------------------------------------------------------------

//渲染进程 (网页) 中
const { ipcRenderer } = require('electron')
//接收主进程回复的消息
ipcRenderer.on('asynchronous-reply', (event, arg) => {
  console.log(arg) // prints "pong"
})
//渲染进程发送异步消息给主进程,
ipcRenderer.send('asynchronous-message', 'ping')

//接收主进程发送来的消息
ipcRenderer.on('main', (event, arg) => {
  console.log(arg) // prints { type: 'video', options: 'video_01' }
})

分析

1.监听名称’asynchronous-message’和’asynchronous-reply’只要对应发送消息时起的名称即可,可以任意自定义字符串

2.消息监听的代码一定要先执行,才能在对应的消息发送后收到

3.消息内容也可以为对象/数字,通常使用对象,官网的例子直接用’ping’字符串只做简单演示

4.这里有个小技巧:send发送消息的时候自定义名称虽然自由,但是不推荐一种消息就起一个名字,我推荐名称尽量少,而使用发送的消息内容里 使用一个type属性来做消息类别的区分, 与使用websocket / postMessage 等处理消息内容时一样,自定义变量区分,方便统一管理

如下:

ipcRenderer.on('main', function (event, arg) {
     const { type, ...argInfos } = arg;
     switch (type) {
        case 'video':
                //other code...
                break;
        default:
                break;
     } 
  });

同步通信

注意:一般为了不阻塞进程,通常采用异步,官方建议异步,除非需要立刻回复消息,可以允许一定的阻塞等待结果,则采用同步.

//主进程中
const { ipcMain } = require('electron')
ipcMain.on('synchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.returnValue = 'pong' //注意回复方式和异步的写法不同
})
//在渲染器进程 (网页) 中
const { ipcRenderer } = require('electron')
const replyValue = ipcRenderer.sendSync('synchronous-message', 'ping')
console.log(replyValue) // prints "pong"

二.electron主进程和webview通信

主进程和webview访客页通信的情况

友情提醒下:以下方法笔者写过是成功的,但是过了一段时间又不管用了,参考了 大神的博客https://www.cnblogs.com/lovesong/p/11180336.html后改为了websocket通信的方案替代了

后面笔者会再详细介绍 electron如何使用websocket代替ipc通信

不过主进程和webview通信可以尝试一下,因为理论上官方是支持的.

step1:

先在webview加载之前preload一个js, 其中把electron对象抛到webview所在访客页的全局,这样可以在访客页调用electron的api,即可以调用ipcRenderer与主进程通信

如下:

<webview id="webview" preload="./js/preload.js" disablewebsecurity nodeintegration autosize="on"></webview>

其中preload.js里挂载全局,并设置监听,然后实际的通信与主进程和渲染进程通信类似

const { ipcRenderer } = require('electron');
window.deskApi = {
        electron: electron
    };

//webview访客页监听主进程的消息
ipcRenderer.on('mainWeb', function (event, arg) {
     const { type, ...argInfos } = arg;
     switch (type) {
        case 'music':
                //other code...
                break;
        default:
                break;
     } 
  });

//webview访客页向主进程发消息
const ipcRenderer = window.deskApi.electron.ipcRenderer
ipcRenderer.send('mainWeb',{ type:'radio',option:'webmesage'})

step2:
主进程这边,还是推荐采用id的形式,在webview的渲染完成的时候提前把的id存储下来,然后需要的时候from(id)就可以

推荐方法:渲染进程中, 在webview dom-ready监听里,把webview的id发送到主进程存储
如下:

const webview = document.querySelector('#webview');
webview.addEventListener('dom-ready', (e) => {
    ///id存储起来,后续指定webview进行通信
    ipcRenderer.send('asynchronous-message', { type: 'webviewContentId',webviewContentId: webview.getWebContentsId()})
}

step3:
最后就在主进程中获取到id, 与访客页通信

//主进程发消息到webview
let webviewContentId= 0;

//接受从渲染进程发来的webviewContentId
ipcMain.on('asynchronous-message', function (event, arg) {
     const { type, ...argInfos } = arg;
     switch (type) {
        case 'webviewContentId':
                webviewContentId = argInfos.webviewContentId
                break;
        default:
                break;
     } 
  });


//接受从webview访客页发来的消息
ipcMain.on('mainWeb', function (event, arg) {
     const { type, ...argInfos } = arg;
     switch (type) {
        case 'radio':
                //other code...
                break;
        default:
                break;
     } 
  });

//根据webview访客页的id发送消息

const { webContents } = require('electron')
const msgObj = { type:'music', options:'music_01' }
webContents.fromId(webviewContentId).send('mainWeb', msgObj)

三.electron渲染进程和webview通信

渲染进程与webview访客页通信和 主进程与webview访客页通信类似

在访客页中:

使用全局的ipcRenderer通信发消息,不过使用的方法有点区别,有两种用法:

用法一: sendToHost 就是发送到 webview访客页所在的html上,即渲染进程中.
用法二: sendTo 可以指定 进程实例webContents的Id,就像上面的那个webview id一样,主进程/渲染进程/webview访客页 都有id

参考官网: https://electronjs.org/docs/api/ipc-renderer

参考主进程和webview通信,一样的preload.js,electron抛全局,这里简写

例:

//webview访客页监听渲染进程的消息
ipcRenderer.on('renderWeb', function (event, arg) {
     const { type, ...argInfos } = arg;
     switch (type) {
        case 'music':
                //other code...
                break;
        default:
                break;
     } 
  });

//用法一: webview访客页向所在的渲染进程发消息
ipcRenderer.sendToHost('renderWeb',{ type:'radio',option:'webmesage'})

//用法二: webview访客页向任意渲染进程发消息,webContentsId自行存储/获取
ipcRenderer.sendTo(webContentsId, 'renderWeb',{ type:'radio',option:'webmesage'})

渲染进程中:

获取到当前主页中(如: index.html)的webvie元素, 使用这个webview实例,向webview发送消息

//渲染进程接受从webview访客页发来的消息
ipcRenderer.on('renderWeb', function (event, arg) {
     const { type, ...argInfos } = arg;
     switch (type) {
        case 'radio':
                //other code...
                break;
        default:
                break;
     } 
  });

//渲染进程向webview访客页发消息
const msgObj = { type:'music', options:'music_01' }

const webview = document.querySelector('#webview');
webview.send('renderWeb', msgObj)

总结

时间有限,行文粗糙,还请谅解.

不过基本思路方法都在那里了,其他细节大家先自行搜索下,后期得空笔者会上案例源码.大家有问题积极留言。

官网文档:https://electronjs.org/docs
https://appsoftea.com/zh/electron-ipcmain-ipcrenderer/
感谢: https://www.cnblogs.com/suzhen-2012/p/9932662.html