Electron 主进程和渲染进程如何通信?这篇讲清楚了

Electron IPC Main API 知识点总结

目录

  • [1. IPC 通信概述](#1. IPC 通信概述 "#1-ipc-%E9%80%9A%E4%BF%A1%E6%A6%82%E8%BF%B0")
  • [2. IPC 通信架构](#2. IPC 通信架构 "#2-ipc-%E9%80%9A%E4%BF%A1%E6%9E%B6%E6%9E%84")
  • [3. 监听方法](#3. 监听方法 "#3-%E7%9B%91%E5%90%AC%E6%96%B9%E6%B3%95")
  • [4. 消息发送模式](#4. 消息发送模式 "#4-%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81%E6%A8%A1%E5%BC%8F")
  • [5. invoke/handle 模式(推荐)](#5. invoke/handle 模式(推荐) "#5-invokehandle-%E6%A8%A1%E5%BC%8F%E6%8E%A8%E8%8D%90")
  • [6. IpcMainEvent 对象](#6. IpcMainEvent 对象 "#6-ipcmainevent-%E5%AF%B9%E8%B1%A1")
  • [7. 最佳实践](#7. 最佳实践 "#7-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5")

1. IPC 通信概述

什么是 IPC?

IPC(Inter-Process Communication) 是 Electron 中主进程渲染进程之间通信的机制。

javascript 复制代码
┌─────────────────────────────────────────────────────┐
│                    主进程 (Main)                    │
│                                                     │
│  const { ipcMain } = require('electron')            │
│                                                     │
│  ipcMain.handle('channel', handler)                │
│      ↓ 接收请求                                      │
│      ↓ 返回结果                                      │
└─────────────────────┬───────────────────────────────┘
                      │ IPC
┌─────────────────────↓───────────────────────────────┐
│                 渲染进程 (Renderer)                  │
│                                                     │
│  const { ipcRenderer } = require('electron')       │
│                                                     │
│  ipcRenderer.invoke('channel', ...args)            │
└─────────────────────────────────────────────────────┘

2. IPC 通信架构

通信方向

方向 方法 说明
渲染 → 主 ipcRenderer.send() 异步发送
渲染 → 主 ipcRenderer.invoke() 异步等待响应
主 → 渲染 webContents.send() 主进程主动推送
渲染 → 主 → 渲染 event.reply() 回复发送者

3. 监听方法

3.1 基础监听 ipcMain.on()

javascript 复制代码
const { ipcMain } = require('electron')

ipcMain.on('message', (event, arg1, arg2) => {
  console.log('收到消息:', arg1, arg2)
})

3.2 一次性监听 ipcMain.once()

javascript 复制代码
ipcMain.once('single-use', (event, data) => {
  console.log('只会触发一次')
})

3.3 移除监听

javascript 复制代码
// 移除特定监听器
ipcMain.off('channel', handlerFunction)

// 移除 channel 的所有监听器
ipcMain.removeAllListeners('channel')

// 移除所有监听器
ipcMain.removeAllListeners()

4. 消息发送模式

4.1 同步消息(不推荐)

javascript 复制代码
// 主进程
ipcMain.on('sync-message', (event) => {
  event.returnValue = '同步响应'  // 直接返回值
})
javascript 复制代码
// 渲染进程
const result = ipcRenderer.sendSync('sync-message')
console.log(result)  // '同步响应'

⚠️ 注意:同步消息会阻塞渲染进程,建议使用 invoke/handle 模式

4.2 异步消息 + 回复

javascript 复制代码
// 主进程
ipcMain.on('async-message', (event, data) => {
  console.log('收到:', data)
  
  // 回复发送者
  event.reply('response', '处理完成')
})
javascript 复制代码
// 渲染进程
ipcRenderer.send('async-message', { name: 'test' })

ipcRenderer.on('response', (event, data) => {
  console.log('收到回复:', data)
})

4.3 从子窗口/iframe 发送

event.reply() 会自动处理从非主 frame 发送的消息:

javascript 复制代码
// 主进程
ipcMain.on('from-iframe', (event) => {
  // event.sender 就是 WebContents
  // 可以发送到主 frame
  event.sender.send('to-main-frame', data)
})

5. invoke/handle 模式(推荐)⭐

这是最推荐的通信方式,支持 Promise。

主进程:定义处理程序

javascript 复制代码
const { ipcMain } = require('electron')

ipcMain.handle('get-user-data', async (event, userId) => {
  // event: IpcMainInvokeEvent
  console.log('请求来自:', event.senderFrame.origin)
  
  const userData = await fetchUserFromDatabase(userId)
  return userData  // 返回值会作为 Promise 的结果
})

渲染进程:调用

javascript 复制代码
const { ipcRenderer } = require('electron')

async function loadUser(userId) {
  try {
    const user = await ipcRenderer.invoke('get-user-data', userId)
    console.log('用户数据:', user)
  } catch (error) {
    console.error('获取失败:', error.message)
  }
}

单次 handle

javascript 复制代码
// 只处理一次,然后自动移除
ipcMain.handleOnce('single-invoke', async (event) => {
  return { once: true }
})

移除 handle

javascript 复制代码
ipcMain.removeHandler('get-user-data')

错误处理

javascript 复制代码
// 主进程
ipcMain.handle('might-fail', async (event, data) => {
  try {
    const result = await riskyOperation(data)
    return result
  } catch (error) {
    throw new Error('操作失败: ' + error.message)
  }
})

// 渲染进程
const result = await ipcRenderer.invoke('might-fail', data)
// 如果主进程抛出异常,这里会 reject

6. IpcMainEvent 对象

监听器接收的 event 对象包含:

属性 说明
event.sender 发送消息的 WebContents
event.senderFrame 发送消息的 Frame
event.reply() 回复消息给发送者
event.returnValue 设置同步消息的返回值
event.preventDefault() 阻止默认行为

使用示例

javascript 复制代码
ipcMain.on('some-event', (event, ...args) => {
  // 回复消息
  event.reply('response-channel', { status: 'ok' })
  
  // 获取发送者信息
  const frame = event.senderFrame
  console.log('来源:', frame?.origin)
  
  // 主动向发送者推送消息
  event.sender.send('push-message', '来自主进程')
})

7. 最佳实践

7.1 推荐:使用 invoke/handle

javascript 复制代码
// ✅ 推荐:清晰的 Promise 风格
ipcMain.handle('fetch-data', async (event, id) => {
  return await database.query(id)
})

// ❌ 避免:同步阻塞
ipcMain.on('sync-fetch', (event) => {
  event.returnValue = database.querySync(id)
})

7.2 封装 IPC 调用

javascript 复制代码
// preload.js - 安全暴露 API
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  // 单向通知
  send: (channel, data) => ipcRenderer.send(channel, data),
  
  // 监听消息
  on: (channel, callback) => {
    ipcRenderer.on(channel, (event, ...args) => callback(...args))
  },
  
  // 双向通信(推荐)
  invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args)
})
javascript 复制代码
// 渲染进程中使用
// 安全的双向通信
const userData = await window.electronAPI.invoke('get-user', userId)

// 发送通知
window.electronAPI.send('user-logged-in', { userId })

7.3 命名规范

javascript 复制代码
// 推荐:使用命名空间格式
'window:minimize'
'window:maximize'
'file:open'
'file:save'
'settings:get'
'settings:set'

// 避免:随意命名
'message'
'data'
'get'

7.4 错误处理

javascript 复制代码
// 主进程
ipcMain.handle('risky-operation', async (event, data) => {
  if (!data) {
    throw new Error('数据不能为空')
  }
  // ... 业务逻辑
})

// 渲染进程
try {
  const result = await ipcRenderer.invoke('risky-operation', data)
} catch (error) {
  console.error('操作失败:', error.message)
}

7.5 清理监听器

javascript 复制代码
// 组件卸载时清理
onUnmounted(() => {
  ipcRenderer.removeAllListeners('channel-name')
})

// 或使用 once 避免手动清理
ipcRenderer.once('one-time-event', (event, data) => {
  // 处理后自动移除
})

完整示例

主进程 (main.js)

javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron')

let mainWindow

app.whenReady().then(() => {
  mainWindow = new BrowserWindow()
  
  // 处理获取数据请求
  ipcMain.handle('get-config', async (event, key) => {
    return { theme: 'dark', language: 'zh-CN' }
  })
  
  // 处理保存数据
  ipcMain.handle('save-config', async (event, config) => {
    await saveToFile(config)
    return { success: true }
  })
  
  // 监听窗口控制
  ipcMain.on('window-minimize', () => {
    mainWindow.minimize()
  })
  
  ipcMain.on('window-maximize', () => {
    if (mainWindow.isMaximized()) {
      mainWindow.unmaximize()
    } else {
      mainWindow.maximize()
    }
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

预加载脚本 (preload.js)

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  getConfig: (key) => ipcRenderer.invoke('get-config', key),
  saveConfig: (config) => ipcRenderer.invoke('save-config', config),
  minimize: () => ipcRenderer.send('window-minimize'),
  maximize: () => ipcRenderer.send('window-maximize'),
  onUpdate: (callback) => ipcRenderer.on('config-updated', callback)
})

渲染进程 (renderer.js)

javascript 复制代码
// 获取配置
const config = await window.electronAPI.getConfig('theme')

// 保存配置
await window.electronAPI.saveConfig({ theme: 'light' })

// 窗口控制
window.electronAPI.minimize()
window.electronAPI.maximize()

// 监听更新
window.electronAPI.onUpdate((newConfig) => {
  console.log('配置已更新:', newConfig)
})

方法速查表

方法 说明 返回值
ipcMain.on(channel, fn) 持续监听 -
ipcMain.once(channel, fn) 单次监听 -
ipcMain.off(channel, fn) 移除监听 -
ipcMain.handle(channel, fn) 处理 Invoke 请求(推荐) Promise
ipcMain.handleOnce(channel, fn) 单次处理 Invoke Promise
ipcMain.removeHandler(channel) 移除处理程序 -
ipcMain.removeAllListeners(channel) 移除所有监听器 -

文档基于 Electron v28+ IPC Main API 编写

相关推荐
前端那点事5 小时前
Vue3+TS 封装高复用 ECharts 通用组件,自适应+防抖+主题切换,开箱即用
前端·vue.js
七十二時_阿川5 小时前
从零到精通:Electron 窗口管理高级技巧
前端·electron
前端那点事5 小时前
Vue3+TS动态路由终极方案|后端权限、刷新不丢、按钮权限、解决所有404BUG
前端·vue.js
前端那点事5 小时前
Vue3+TS手写不定高虚拟列表Hooks,彻底解决长列表卡顿,生产直接复用
前端·vue.js
ZC跨境爬虫5 小时前
跟着 MDN 学 HTML day_61:(构建反馈表单的结构化挑战)
前端·javascript·ui·html·音视频
卷帘依旧5 小时前
Vue2中defineProperty缺陷
前端
长安第一美人5 小时前
工业级实时监控系统开发:PHP+ZMQ+JS 前后端分离架构全解析
前端·嵌入式硬件·架构·交互·rk3588·zmq后端
ricardo19736 小时前
资源加载提速四件套:dns-prefetch / preconnect / preload / prefetch 实战
前端·面试
豹哥学前端6 小时前
JavaScript 异步编程完全指南:从回调地狱到 async/await,一次通关
前端·javascript·面试