天啦!原来electron进程间通讯这么简单~

347 阅读3分钟

主进程->渲染进程

sandbox沙盒化配置,默认值false,即不开启沙盒化。nodeIntegration即node环境集成,默认值false,contextIsolation隔离上下文,默认值true。 当主进程不添加任何配置的话,默认处于上下文隔离状态,此时无法在preload(预加载脚本)中访问node API。当设置sandbox:true和nodeIntegration:true,此时沙盒化和覆盖掉node集成配置,同样也无法在preload(预加载脚本)中访问node API。单独设置sandbox:false或者nodeIntegration:true的话,则可以破除限制,在preload(预加载脚本)中可访问node API。

主进程,这里以读取本地json文件内容为例
const { ipcMain } = require('electron')
ipcMain.on('readConfig', (e) => {
    fs.readFile('/electron-vite-vue/config.json', 'utf8', (err, data) => {
      if (err) {
        console.err(err)
      } else {
        e.sender.send('getConfig', data)
      }
    })
  })

子进程,先向主进程发送正在监听的监听事件,然后再监听主进程发送过来的消息。
const { ipcRenderer } = require('electron')
ipcRenderer.send('readConfig')
ipcRenderer.on('getConfig', (ev, msg) => {
  console.log('来自主进程的消息', msg)
})

渲染进程->主进程

情况一,渲染向主发送消息,主接收。

渲染进程:
const { ipcRenderer } = require('electron')

ipcRenderer.send('test1', '我是渲染进程')
主进程:
const { ipcMain } = require('electron')
ipcMain.on('test1',(e,msg)=>{
   console.log('receive renderer message',msg)
})

情况二,渲染向主发送消息,主接收后,再回复渲染

渲染进程:
const { ipcRenderer } = require('electron')

ipcRenderer.send('test1', '我是渲染进程')
渲染进程监听主进程回复的
ipcRenderer.on('test2', (_, arg) => {
    console.log('receive main message', arg)
})
主进程:
const { ipcMain } = require('electron')
ipcMain.on('test1',(e,msg)=>{
   console.log('receive renderer message',msg)
   //下面两种发送都可以
   e.sender.send('test2','我是主进程');
   e.reply('test2','我是主进程')
})

情况三,渲染向主发送消息,主接收后,再回复消息给渲染,渲染等待拿到主回复,返回值状态改变

渲染进程:
注意,渲染进程通过 `ipcRenderer.invoke` 发送消息后,`invoke` 的返回值是一个 `Promise<pending>` 。主进程回复
消息需要通过 `return` 的方式进行回复,而 `ipcRenderer` 只需要等到 `Promise resolve` 即可获取到返回的值。

const { ipcRenderer } = require('electron')
async function invokeMsgToMain(){
   const result=await ipcRenderer.invoke('test3','我是渲染进程')
   console.log('接收主进程消息',result)
}
invokeMsgToMain()
主进程:
const { ipcMain } = require('electron')
ipcMain.handle('test3', (_, msg) => {
   console.log('receive renderer message', msg)
   return message
})

情况四,注意,渲染进程通过ipcRenderer.sendSync发送消息后,主进程回复消息需要通过e.returnValue 的方式进行回复,如果 event.returnValue 不为 undefined 的话,渲染进程会等待 sendSync 的返回值才执行后面的代码。

渲染进程:
这里页面会卡住,等待3秒后获取到主进程回复后页面渲染恢复正常
const { ipcRenderer } = require('electron')
async function sendSyncMessageToMain() {
   const replyMessage = await ipcRenderer.sendSync('test4', '我是来自渲染进程的消息')
   console.log('接收主进程消息', replyMessage)
}
sendSyncMessageToMain()
主进程:
ipcMain.on('test4', (ev, msg) => {
   console.log('接收渲染进程消息', msg)
   setTimeout(() => {
     ev.returnValue = '我是来自主进程的回复'
   }, 3000)
})

小结: ipcRenderer.send: 这个方法是异步的,用于从渲染进程向主进程发送消息。它发送消息后不会等待主进程的响应,而是立即返回,适合在不需要等待主进程响应的情况下发送消息。

ipcRenderer.sendSync:ipcRenderer.send 不同,这个方法是同步的,也是用于从渲染进程向主进程发送消息,但是它会等待主进程返回响应。它会阻塞当前进程,直到收到主进程的返回值或者超时。

ipcRenderer.invoke: 这个方法也是用于从渲染进程向主进程发送消息,但是它是一个异步的方法,可以方便地在渲染进程中等待主进程返回 Promise 结果。相对于 sendsendSync,它更适合处理异步操作,例如主进程返回 Promise 的情况。

安全通讯方案:

通过预加载脚本preload.js作为中间桥梁进行通讯,这也是官方推荐的方案。

预加载脚本preload.js
import { contextBridge } from 'electron'
这里补充一点,@electron-toolkit/preload是第三方插件,需下载,内部集成了ipcRenderer、process相关api供其使用
import { electronAPI } from '@electron-toolkit/preload'

if (process.contextIsolated) {
  try {
    contextBridge.exposeInMainWorld('electron', { electronAPI })
  } catch (error) {
    console.error(error)
  }
} else {
  window.electron = electronAPI
}
渲染进程:
electron.electronAPI.ipcRenderer.send('readConfig')

electron.electronAPI.ipcRenderer.on('getConfig', (ev, msg) => {
  console.log('来自主进程的消息', msg)
})
主进程:
ipcMain.on('readConfig', (e) => {
    fs.readFile('/electron-vite-vue/config.json', 'utf8', (err, data) => {
      if (err) {
        console.err(err)
      } else {
        e.sender.send('getConfig', data)
      }
    })
  })