前言

在 Electron 开发中,主进程与渲染进程的通信是构建桌面应用的核心能力。随着 Electron 版本的迭代,通信方式也在不断优化。本文将以 Vue3 项目为例,对比传统 send 模式与现代 invoke 模式,并给出最佳实践方案。


一、渲染进程到主进程的通信的两种模式

传统模式:ipcRenderer.send+ ipcMain.on

适用场景:简单的单向通信(如渲染进程通知主进程执行操作,不关心结果)。

实现步骤

主进程(main.js)

	const { ipcMain } = require('electron');
	ipcMain.on('log-message', (event, message) => {
	  console.log('收到渲染进程消息:', message);
	  // 回复消息(可选)
	  event.reply('log-reply', '消息已接收');
	});

预加载脚本(preload.js)

	const { contextBridge, ipcRenderer } = require('electron');
	contextBridge.exposeInMainWorld('electronAPI', {
	  sendLogMessage: (message) => ipcRenderer.send('log-message', message),
	  onLogReply: (callback) => ipcRenderer.on('log-reply', (_, reply) => callback(reply)),
	});

渲染进程(Vue3 组件)

	<template>
	  <div>
	    <button @click="sendMessage">发送消息</button>
	    <div v-if="reply">主进程回复:{{ reply }}</div>
	  </div>
	</template>	 
	<script setup>
	import { ref } from 'vue';
	const reply = ref('');
	const sendMessage = () => {
	  window.electronAPI.sendLogMessage('Hello from Renderer!');
	};
	 
	// 监听主进程回复
	window.electronAPI.onLogReply((res) => {
	  reply.value = res;
	});

	</script>

特点:

  • 代码复杂:需要手动管理消息通道和回复逻辑。
  • 无返回值send 本身不返回 Promise,需通过 event.replysend 的另一个实例实现双向通信。
  • 适合场景:简单的通知或不需要返回值的操作。

现代模式:ipcRenderer.invoke + ipcMain.handle

适用场景:需要返回结果的双向通信(如文件操作、系统信息查询)。

实现步骤

主进程(main.js)

	const { ipcMain } = require('electron');
	ipcMain.handle('get-system-info', async () => {
	  return {
	    platform: process.platform,
	    arch: process.arch,
	    version: process.versions.electron,
	  };
	});

预加载脚本(preload.js)

	contextBridge.exposeInMainWorld('electronAPI', {
	  getSystemInfo: () => ipcRenderer.invoke('get-system-info'),
	});

渲染进程(Vue3 组件)

	<template>
	  <div>
	    <button @click="fetchSystemInfo">获取系统信息</button>
	    <div v-if="info">系统信息:{{ info }}</div>
	  </div>
	</template>
	<script setup>
	import { ref } from 'vue';
	const info = ref(null);
	
	const fetchSystemInfo = async () => {
	  try {
	    info.value = await window.electronAPI.getSystemInfo();
	  } catch (error) {
	    console.error('获取系统信息失败:', error);
	  }
	};

	</script>

特点:

  • 代码简洁:基于 Promise,逻辑更清晰。
  • 直接返回值invoke 返回 Promise,适合需要返回结果的场景。
  • 错误处理方便:可直接用 try/catch 捕获错误。
  • 推荐场景:文件操作、系统信息查询、数据库查询等。

如何选择通信模式?

场景 推荐模式 原因
渲染进程通知主进程(无返回值) send + on 简单直接,无需返回值
渲染进程请求主进程返回数据 invoke + handle 代码简洁,支持 Promise 和错误处理

二、主进程向渲染进程通信的方式

webContents.send(推荐方式)

适用场景:主进程主动推送数据到渲染进程(如状态更新、实时通知)。

实现步骤

主进程(main.js)

	const { BrowserWindow, app } = require('electron');
	let mainWindow;
	app.whenReady().then(() => {
	  mainWindow = new BrowserWindow({ /* 窗口配置 */ });
	  // 模拟主进程主动推送数据
	  setInterval(() => {
	    // 发送消息到渲染进程
	    mainWindow.webContents.send('progress-update', {
	      percent: Math.floor(Math.random() * 100), // 随机进度
	      status: '处理中...',
	    });

	  }, 1000);

	});

预加载脚本(preload.js)

	const { contextBridge, ipcRenderer } = require('electron');
	contextBridge.exposeInMainWorld('electronAPI', {
	  onProgressUpdate: (callback) => {
	    // 暴露监听器给渲染进程
	    return ipcRenderer.on('progress-update', (_, data) => callback(data));
	  },
	});

渲染进程(Vue3 组件)

	<template>
	  <div>
	    <h2>任务进度</h2>
	    <progress :value="progress.percent" max="100"></progress>
	    <p>{{ progress.status }}</p>
	  </div>
	</template>
	<script setup>
	import { ref, onMounted, onBeforeUnmount } from 'vue';
	const progress = ref({ percent: 0, status: '等待中...' });
	let cleanup = null;
	onMounted(() => {
	  // 监听主进程推送的数据
	  cleanup = window.electronAPI.onProgressUpdate((data) => {
	    progress.value = data;
	  });
	});

	onBeforeUnmount(() => {
	  if (cleanup) cleanup(); // 清理监听器,避免内存泄漏
	});
	</script>

特点:

  • 主动推送:主进程无需等待渲染进程请求,直接发送数据。
  • 实时性强:适合进度条、状态更新等实时场景。
  • 需清理监听器:在渲染进程中,组件卸载时需清理监听器。

总结

通过本文的对比和示例,你可以根据具体场景选择最适合的双向通信方式,为 Vue3 + Electron 开发提供高效、安全的通信方案!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐