electron进程间通信(IPC)

在 Electron 中,使用 ipcMainipcRenderer 模块来处理进程间通信。

  • 在主进程中,可以使用 ipcMain 模块监听事件,通过 ipcMain.on() 方法注册事件处理程序,接收渲染进程发送的消息,并通过 event.sender.send() 方法向渲染进程发送回复。

  • 在渲染进程中,可以使用 ipcRenderer 模块发送消息,通过 ipcRenderer.send() 方法发送消息给主进程,并使用 ipcRenderer.on() 方法监听主进程发送的消息。

由于渲染进程中默认无法使用NodeJS API,也就无法使用 require 导入模块,所以我们需要将 ipcRenderer 模块的相关内容在预处理脚本中暴露,才能在渲染进程中使用。

渲染进程向主进程通信(单向)

// main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path');

app.on('ready', () => {
   const win = new BrowserWindow({
       width: 800,
       height: 600,
       webPreferences: {
           preload: path.join(__dirname, 'p1.js')
       },
   });
   // 监听 fromSon 事件(频道)
   ipcMain.on('fromSon', function (event, arg1) {
       console.log(arg1);
       // 在主进程中设置窗口的标题
       win.setTitle(arg1)
   })
   win.loadFile('index.html')
})
// p1.js (预加载脚本)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('elecAPI', {
   // 定义sendToFather方法,该方法可以在渲染进程中使用
   sendToFather: function (val) {
       // 使用ipcRenderer.send()方法向主进程指定频道发送信息
       ipcRenderer.send('fromSon', val)
   }
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>您好,世界</title>
</head>
<body>
   <h1>您好,世界!</h1>
   <button id="btn">发送</button>
   <script>
       let btn = document.getElementById('btn');
       btn.onclick = function () {
           // 调用预处理脚本中定义的方法,向主进程发送数据
           elecAPI.sendToFather('来自渲染进程的问候')
       }
   </script>
</body>
</html>

上面的代码中,我们在主进程中使用 ipcMain.on()方法监听 fromSon 频道(事件)。在渲染进程中使用 ipcRenderer.send() 方法向fromSon 频道发送数据。


渲染进程和主进程双向通信

这可以通过 ipcRenderer.invokeipcMain.handle 搭配使用来完成双向通信。

  • ipcRenderer.invoke() 方法允许渲染进程向主进程发送请求,并等待主进程返回结果。

  • ipcMain.handle() 方法可以为指定频道注册处理函数,这个处理函数可以接收请求的参数并执行相应的操作,然后返回一个结果给渲染进程。

// main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path');

app.on('ready', () => {
   const win = new BrowserWindow({
       width: 800,
       height: 600,
       webPreferences: {
           preload: path.join(__dirname, 'p1.js')
       },
   });
   // 在主进程中监听 fromSon 事件,并为它绑定个处理函数。
   // 在处理函数中return的值就是返回给渲染进程的数据。
   ipcMain.handle('fromSon', function (event, arg1) {
       console.log(arg1);
       return '您的问候已收到,这是我的回复'
   })
   win.loadFile('index.html')
})
// p1.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('elecAPI', {
   // 定义sendToFather方法,该方法可以在渲染进程中使用
   sendToFather: function (val) {
       // 使用ipcRenderer.invoke()方法向主进程指定频道发送信息,它会返回一个Promise
       return ipcRenderer.invoke('fromSon', val)
   }
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>您好,世界</title>
</head>
<body>
   <h1>您好,世界!</h1>
   <button id="btn">发送</button>
   <script>
       let btn = document.getElementById('btn');
       btn.onclick = async function () {
           // 调用预处理脚本中定义的方法,向主进程发送数据,并接收返回值
           let res = await elecAPI.sendToFather('来自渲染进程的问候')
           console.log(res);
       }
   </script>
</body>
</html>


主进程向渲染进程通信(单向)

将消息从主进程发送到渲染进程时,需要指定是哪一个渲染进程接收消息。 消息需要通过该渲染进程的 WebContents 实例发送到渲染进程。 此 WebContents 实例包含一个 send 方法,其使用方式与 ipcRenderer.send 相同。

// main.js
const { app, BrowserWindow } = require('electron')
const path = require('path');

app.on('ready', () => {
   const win = new BrowserWindow({
       width: 800,
       height: 600,
       webPreferences: {
           preload: path.join(__dirname, 'p1.js')
       },
   });
   win.loadFile('index.html')

   // 启动5秒后,向渲染进程发送数据
   setTimeout(function () {
       // 获取渲染进程的 WebContents,用使用它的send方法向渲染进程发送数据
       win.webContents.send('toSon', '来自主进程的问候')
   }, 5000)
})
// p1.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('elecAPI', {
   // 定义 fromFather方法,该方法可以在渲染进程中使用
   fromFather: function (callback) {
       // 使用ipcRenderer.on() 方法接收指定频道传来的数据,并用我们传入的处理函数处理它
       ipcRenderer.on('toSon', callback)
   }
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>您好,世界</title>
</head>
<body>
   <h1>您好,世界!</h1>
   <script>
       // 使用预处理脚本中定义的函数,间接监听从主进程中传开的数据
       elecAPI.fromFather(function (event, arg1) {
           console.log(arg1);
       })
   </script>
</body>
</html>


渲染进程之间的通信

目前没有直接的方法可以进行渲染进程之间的通信,不过可以将主进程作为渲染进程之间的消息代理。 这需要将消息从一个渲染进程发送到主进程,然后主进程将消息转发到另一个渲染进程。或者使用第三方存储方案(如:localStorage、数据库等)进行中转


参考文档:

https://www.electronjs.org/zh/docs/latest/tutorial/ipc

https://www.electronjs.org/zh/docs/latest/api/ipc-main

https://www.electronjs.org/zh/docs/latest/api/ipc-renderer


微信 遇到疑问可加微信进行反映