Electron入门实践(3):进程间通讯

341 阅读4分钟

引言 在之前的文章中,我们了解了Electron应用程序的三个核心部分:主进程、预加载脚本和渲染进程。我们还探讨了如何通过预加载脚本安全地传递消息。在本篇文章中,我们将详细探讨Electron中的进程间通讯机制,特别是ipcMainipcRenderer的使用。

1. 进程间通讯方式:ipcMain和ipcRenderer的交互

Electron提供了ipcMainipcRenderer这两个模块来实现主进程和渲染进程之间的通讯。这两个模块提供了不同的API,用于发送和接收消息。

主进程中的 ipcMain

在主进程中,ipcMain 用于监听来自渲染进程的消息,以及向渲染进程发送消息,有下面几个api(基本使用)。

  • ipcMain.on:监听来自渲染进程的消息
    // 主进程中的代码
    const { ipcMain } = require('electron');
    ipcMain.on('messageFromRenderer', (event, message) => {
      console.log('主进程收到来自渲染进程的消息:', message);
    });
    
  • ipcMain.once:监听来自渲染进程的消息,与ipcMain.on类似,但它只监听一次事件。一旦事件被触发,监听器就会被移除。
    // 主进程中的代码
    const { ipcMain } = require('electron');
    ipcMain.once('messageFromRenderer', (event, message) => {
      // 只监听到一次
      console.log('主进程收到来自渲染进程的消息:', message);
    });
    
  • ipcMain.send:向渲染进程发送消息
    // 主进程中的代码
    ipcMain.send('messageToRenderer', 'Hello from main!');
    
  • ipcMain.handle:处理渲染进程的请求
    // 主进程中的代码
    ipcMain.handle('getUserData', async (event, userId) => {
      // 处理请求并返回用户数据
      const userData = await fetchUserData(userId);
      // 把数据返回给渲染进程
      return userData;
    });
    

渲染进程中的 ipcRenderer

在渲染进程中,ipcRenderer 用于向主进程发送消息,以及接收主进程发送的消息。

  • ipcRenderer.send:向主进程发送消息
    // 渲染进程中的代码
    const { ipcRenderer } = require('electron');
    ipcRenderer.send('messageToMain', 'Hello from renderer!');
    
  • ipcRenderer.on:接收主进程发送的消息
    // 渲染进程中的代码
    ipcRenderer.on('messageFromMain', (event, message) => {
      console.log('渲染进程收到主进程的消息:', message);
    });
    
  • ipcRenderer.invoke:调用主进程的API
    // 渲染进程中的代码
    ipcRenderer.invoke('getUserData', userId).then((userData) => {
      console.log('渲染进程收到用户数据:', userData);
    });
    

2. 详细的通讯方式示例

为了确保安全性和隔离性,渲染进程不应该直接使用ipcRenderer。相反,我们应该通过预加载脚本暴露的API与主进程通信。以下有两个例子:

  • 主进程监听并响应渲染进程的消息

使用ipcMain.on,主进程可以监听来自渲染进程的消息。

// 主进程中的代码
ipcMain.on('messageFromRenderer', (event, message) => {
  console.log('主进程收到来自渲染进程的消息:', message);
});

预加载脚本作为主进程和渲染进程的桥梁,为渲染进程提供调用方法。

// 预加载脚本preload.js
const { contextBridge, ipcRenderer } = require('electron');
const api = {
  sendMessageFromRenderer: (message) => ipcRenderer.send('messageFromRenderer', message),
  receiveMessageFromMain: (cb) => ipcRenderer.on('messageFromMain', cb),
};
if (process.contextIsolated) {
  try {
    contextBridge.exposeInMainWorld('api', api);
  } catch (error) {
    console.error(error);
  }
} else {
  window.electronAPI = api;
}

在渲染进程中,我们可以通过使用预加载脚本挂载到window上的api对象方法,来对主线程发送消息:

// 渲染进程中的代码
window.api.sendMessageFromRenderer('Hello from renderer!');

主进程处理完消息后,还可以通过event.reply向渲染进程发送响应:

// 主进程中的代码
ipcMain.on('messageFromRenderer', (event, message) => {
  console.log('主进程收到来自渲染进程的消息:', message);
  // 发送响应到渲染进程
  event.reply('messageFromMain', 'Response from main process');
});

在渲染进程中,我们可以通过使用预加载脚本挂载到window上的api对象方法,来监听这个响应:

// 渲染进程中的代码
window.api.receiveMessageFromMain((event, response) => {
  console.log('渲染进程收到主进程的响应:', response);
});
  • 渲染进程调用主进程的API

使用ipcMain.handle,主进程可以定义一个API,用于处理渲染进程的请求。

// 主进程中的代码
ipcMain.handle('getData', async (event, userId) => {
  // 在这里处理请求,并返回数据
  const data = await fetchData(userId);
  return data;
});

预加载脚本使用ipcRenderer.invoke来进行调用。

// 预加载脚本preload.js
const { contextBridge, ipcRenderer } = require('electron');
const api = {
  getData: (id) => ipcRenderer.invoke('getData', id)
};
if (process.contextIsolated) {
  try {
    contextBridge.exposeInMainWorld('api', api);
  } catch (error) {
    console.error(error);
  }
} else {
  window.electronAPI = api;
}

在渲染进程中,我们可以这样调用这个API:

// 渲染进程中的代码
async function getData(id) {
  try {
    const data = await window.api.getData(id);
    console.log('渲染进程收到数据:', data);
  } catch (error) {
    console.error('渲染进程处理数据时出错:', error);
  }
}
// 调用函数获取数据
getData('123');

通过本篇文章,你应该对Electron中的进程间通讯机制有了更深入的了解。我们学习了如何使用ipcMainipcRenderer来实现主进程和渲染进程之间的消息传递。在下一篇文章中,我们将了解了如何在不同进程中使用electron-storelocalforagelocalStorage来管理应用程序的状态和数据。