ipcMain 和 ipcRenderer 模块

April 1, 2020 · View on GitHub

  Electron 使用 IPC(interprocess communication)在进程间通信,与 Chromium 相同。IPC 可以在渲染进程和主进程之间双向通信。IPC 默认是异步的,但也有同步的 API(如 Node.js 中的 fs)。

异步通信

  • 主进程 => 渲染进程: 主进程发送异步事件
const mainWindow = new BrowserWindow({})
mainWindow.send(eventName, args)
  • 渲染进程 <= 主进程: 渲染进程监听事件
ipcRenderwer.on(eventName, (event, args) => {})
  • 渲染进程 => 主进程:渲染进程发送异步事件
ipcRenderer.send(eventName, args)
  • 主进程 <= 渲染进程:主进程监听事件

  主进程在ipcMain监听回调里,通过event.reply(eventName, repleyArgs)响应事件。

ipcMain.on(eventName, (event, args) => {
  // 响应事件
  event.reply(eventName, 'repley')
})

同步通信

  同步通讯与异步通信类似,主要有以下不同:

  • 事件发送方法不再是send,而是sendSync
const mainWindow = new BrowserWindow({})
const result = mainWindow.sendSync(eventName, args)
  • 监听响应不再是event.sender.send,而是直接赋值event.returnValue
ipcMain.on(eventName, (event, args) => {
  event.returnValue = '回复';
})

示例

index.html

<button id="syncBtn">同步发送</button>
<button id="asyncBtn">异步发送</button>
<script src="renderer.js"></script>

渲染进程:renderer.js

const { ipcRenderer } = require('electron')

function $(dom) {
  return document.querySelector(dom)
}

$('#syncBtn').addEventListener('click', () => {
  const result = ipcRenderer.sendSync('syncSend', '同步sync发送消息');
  console.log('主进程响应sync消息:', result);
})

$('#asyncBtn').addEventListener('click', () => {
  ipcRenderer.send('asyncSend', '同步async发送消息');
  ipcRenderer.on('reply', (event, args) => {
    console.log('主进程响应async消息:', args);
  })
})

主进程:main.js

const { app, BrowserWindow, ipcMain } = require('electron')

function createWindow() {
  const mainWin = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  mainWin.webContents.openDevTools({
    mode: 'bottom'
  });
  mainWin.loadFile('index.html')

  ipcMain.on('syncSend', (event, args) => {
    console.log('ipcRenderer syncSend消息:', args);
    event.returnValue = '主进程收到同步消息';
  })

  ipcMain.on('asyncSend', (event, args) => {
    console.log('ipcRenderer asyncSend消息:', args);
    event.reply('reply', '主进程收到异步消息')
  })
}

app.on('ready', createWindow)

remote 模块

  Electron 还提供了 remote 模块,使渲染进程中可以访问/使用主进程模块。

在渲染进程新建窗口

const { remote, remote: { BrowserWindow } } = require('electron')

const win = new BrowserWindow({
  width: 600,
  height: 500
})

win.loadURL('https://github.com/staven630')

渲染进程访问主进程模块

show-dialog.js

const electron = require('electron')
const dialog = electron.dialog || electron.remote.dialog

function showDialog(message) {
  dialog.showMessageBox({
    type: 'info',
    title: '提示',
    message,
    buttons: ['确认', '提示']
  })
}

module.exports = showDialog

main.js

const { app } = require('electron')
const showDialog = require('./show-dialog')
showDialog()

renderer.js

const showDialog = require('./show-dialog')

document.querySelector('#open-dialog').addEventListener('click', () => {
  showDialog('staven')
})

  通过 remote 对象,可以不用显示地发送进程间消息来进行通信。但实际上,调用远程对象的方法、函数或者通过远程构造函数创建一个新的对象,实际上都是在发送一个同步的进程间消息。

  如果在主进程中进行 CPU 密集型工作,它将锁定所有渲染器进程。因此,CPU 密集型任务应该在一个单独的进程中运行,而不是具有 UI 的现有渲染器。

devtron 模块

  使用 devtron 模块,可以检测使用 remote 模块时发生的所有 IPC 调用。同步 IPC 调用可能具有性能缺陷性。

网络协议

  Chromium IPC 文档声明它是“named pipes”作为基础工具。named pipes 允许比网络协议提供的更快,更安全的通信。