Electron 进程间通信(IPC)方法详解

news/2025/3/13 18:09:42/文章来源:https://www.cnblogs.com/laozhenHome/p/18768568

Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架,它是基于 Chromium 和 Node.js 构建的,而 Chromium 本身是采用多进程架构的,所以 Electron 也是多进程的。
Electron 是一个多进程框架,它的进程主要分为两类:主进程(Main Process)渲染进程(Renderer Process) ,两者分工协作,共同完成桌面应用的运行。

Electron 主进程

每个 Electron 应用有且仅有一个主进程,它是应用程序的入口点。主进程在 Node.js 环境中运行,这意味着它具有使用 require 模块和所有 Node.js API 的能力,拥有完整的系统权限。主进程常用来负责应用的生命周期管理(启动、退出)、窗口创建、系统事件处理(如文件操作、菜单交互)等。主进程通过 BrowserWindow 创建和管理窗口,每个窗口对应一个独立的渲染进程。

Electron 渲染进程

每个 Electron 应用都会为 BrowserWindow 创建的窗口生成一个单独的渲染器进程。 渲染进程负责渲染网页内容,处理用户界面交互,运行于渲染进程中的代码要遵照网页创建标准。渲染进程以一个 HTML 文件作为渲染器进程的入口点,这也意味着渲染进程中无权直接访问 require 或其他 Node.js API。

进程间通信(IPC)

Electron 提供了一个特殊的预加载脚本,它将 Electron 的主进程和渲染进程桥接在一起,我们可以通过配置预加载脚本来实现 Electron 不同进程之间的通信。

预加载脚本在 BrowserWindow 构造器中使用 webPreferences.preload 引入,它在渲染器加载网页之前注入,所以在预加载脚本中可以访问 document、window、部分权限的 Node.js 和 Electron 的 API。

// main.js 主进程文件const { app, BrowserWindow } = require('electron/main')
const path = require('node:path')const createWindow = () => {// 创建浏览器窗口const win = new BrowserWindow({width: 800, // 窗口宽度height: 600, // 窗口高度webPreferences: {// sandbox: false, // 是否开启沙盒模式// nodeIntegration: false, // 是否开启node集成// contextIsolation: true, // 是否开启上下文隔离preload: path.join(__dirname, 'preload.js'), // 预加载脚本},})win.webContents.openDevTools() // 打开窗口的开发者工具// 把html文件加载到窗口中win.loadFile('index.html')
}

在预加载脚本中,我们通过 Electron 中的 contextBridge 定义一个变量暴露给渲染器,在这个变量中可以添加主进程中的一些 API,渲染器可以在全局 window 对象中访问它。下面是预加载脚本的部分代码,展示的是如何把 Node 和 Electron 的版本号暴露给渲染器。

// preload.js 预加载文件const { contextBridge } = require('electron')contextBridge.exposeInMainWorld('versions', {node: () => process.versions.node, // node版本号chrome: () => process.versions.chrome, // chrome版本号electron: () => process.versions.electron, // electron版本后// 除函数之外,我们也可以暴露变量
})

在 html 文件中我们可以直接获取变量 versions,代码展示如下:

<body><p id="version-info"></p>
</body>
<script>
// 渲染脚本
window.addEventListener('DOMContentLoaded', () => {if (versionInfo) {const versionInfoElement = document.getElementById('version-info')versionInfoElement.innerHTML = `<p>Node.js 版本: v${versionInfo?.node()}</p><p>Chrome 版本: v${versionInfo?.chrome()}</p><p>Electron 版本: v${versionInfo?.electron()}</p>`}
})
</script>

1. 模式一:渲染器进程 => 主进程

通常使用此模式从 web 页面触发预加载脚本中定义的事件,主进程中监听此事件并处理相关内容。下面展示的是设置窗口标题的例子,只部分代码,详情请从 github 下载。

点击查看代码
// main.js 主进程文件
// 监听渲染进程的 set-title 消息,主进程设置窗口标题
ipcMain.on('set-title', (event, title) => {const webContents = event.senderconst win = BrowserWindow.fromWebContents(webContents)win.setTitle(title)
})// preload.js
contextBridge.exposeInMainWorld('electronAPI', {setTitle: title => ipcRenderer.send('set-title', title), // set-title 是自定义的频道名称,主进程监听 set-title 频道并设置窗口标题
})// html
<div class="demo-2 demo-box"><p>修改窗口标题</p><div>标题: <input id="title"/><button id="set-title-btn" type="button">设置标题</button></div>
</div>// setTitle.js 渲染脚本
window.addEventListener('DOMContentLoaded', () => {const setButton = document.getElementById('set-title-btn')const titleInput = document.getElementById('title')setButton.addEventListener('click', () => {const title = titleInput.valueif (window?.electronAPI) {window.electronAPI.setTitle(title)}})
})

2. 模式二:渲染器进程 <= 主进程

我们构建一个由原生操作系统菜单控制的数字计数器,由主进程控制计数器的增减,页面负责更新数据。在主进程中,调用窗口实例的 webContents.send 方法向渲染进程传递数据。

点击查看代码
// main.js 主进程
const { app, BrowserWindow, Menu, ipcMain } = require('electron/main')
const path = require('node:path')const createWindow = () => {const win = new BrowserWindow({width: 800, // 弹窗宽度height: 600, // 弹窗高度webPreferences: {preload: path.join(__dirname, 'preload.js'), // 预加载脚本},})// 打开窗口的开发者工具win.webContents.openDevTools()// 生成原生操作系统菜单const menu = Menu.buildFromTemplate([{label: '计数器',submenu: [{click: () => win.webContents.send('update-counter', 1), // update-counter 是自定义的频道名称label: '加1',},{click: () => win.webContents.send('update-counter', -1),label: '减1',},],},])// 设置窗口菜单Menu.setApplicationMenu(menu)// 把html文件加载到弹窗中win.loadFile('index.html')
}// repload.js 预加载脚本
const { contextBridge, ipcRenderer } = require('electron')contextBridge.exposeInMainWorld('electronAPI', {onUpdateCounter: callback => ipcRenderer.on('update-counter', (_event, value) => callback(value)), // 渲染进程监听 update-counter 频道并更新计数器
})// html
<div class="demo-3 demo-box"><p>计数器:<span id="counter">0</span></p>
</div>// counter.js 计数器渲染脚本
window.addEventListener('DOMContentLoaded', () => {let counter = document.getElementById('counter').innerTextcounter = Number(counter)// 渲染进程监听主进程,当主进程发出 update-counter 消息时,更新计数器if (window.electronAPI?.onUpdateCounter) {window.electronAPI.onUpdateCounter(value => {counter += valuedocument.getElementById('counter').innerText = counter})}
})

页面更新数据后,也可以向主进程发送信息,让主进程知道页面已更新,具体实现方式请查看源码。页面计数器更新及主进程获取更新信息的截图如下:

计数器更新图

3. 模式三:渲染器进程 <=> 主进程

双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。对于两进程间的相互通信,我们可以配合使用 ipcRenderer.invoke 和 ipcMain.handle 方法完成。现在我们实现一个例子,从渲染器进程打开一个原生的文件对话框,并返回所选文件的路径。

点击查看代码
// main.js 主进程
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron/main')
const path = require('node:path')// 创建浏览器窗口
const createWindow = () => {const win = new BrowserWindow({width: 800, // 弹窗宽度height: 600, // 弹窗高度webPreferences: {preload: path.join(__dirname, 'preload.js'), // 预加载脚本},})// 打开窗口的开发者工具win.webContents.openDevTools()// 把html文件加载到弹窗中win.loadFile('index.html')
}app.whenReady().then(() => {createWindow()// 监听渲染进程的 open-file 频道,显示文件选择框ipcMain.handle('open-file', async () => {const { canceled, filePaths } = await dialog.showOpenDialog()if (canceled) {return} else {return filePaths[0]}})// replod.js 预加载脚本
const { contextBridge, ipcRenderer } = require('electron')contextBridge.exposeInMainWorld('electronAPI', {openFile: () => ipcRenderer.invoke('open-file'), // 渲染进程触发通信的频道名称open-file,主进程监听open-file频道并返回文件路径})// html
<div class="demo-4 demo-box"><p><button type="button" id="open-btn">打开文件</button>文件路径:<strong id="filePath"></strong></p>
</div>// openFile.js 渲染脚本
window.addEventListener('DOMContentLoaded', () => {const electronAPI = window.electronAPIif (electronAPI?.openFile) {const openBtn = document.getElementById('open-btn')const pathElement = document.getElementById('filePath')openBtn.addEventListener('click', async () => {const filePath = await electronAPI.openFile()pathElement.innerText = filePath})}
})

主进程与渲染进程之间的通信大概可以通过以上几种方式实现,阅读完代码大家会发现,进程间通信主要使用的是 ipcRenderer.invoke/ipcMain.handle 和 ipcRenderer.send/ipcMain.on 两种不同模式,这两种模式的具体区别及使用场景我们后面再说。

github仓库地址:https://hgithub.xyz/zhench0515/electron-ipc.git 或者 https://github.com/zhench0515/electron-ipc.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/898285.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始的web前端学习-JavaScript

JavaScript 是一种运行在客户端(浏览器)的编程语言,实现人机互动效果:网页特效(监听用户的某些行为并令网页进行反馈) 表单验证(针对表单数据的合法性进行判断) 数据交互(获取后台数据并渲染到前端)JavaScript 组成ECMAScript:基础语法核心 Web APIs:DOM(页面文档…

【Azure Service Bus】分享使用 Python Service Bus SDK 输出SDK内操作日志

问题描述 使用Python代码消费Service Bus中的消息,默认情况 Console 中的信息都是通过 print 打印输出。 有时候需要调查更多SDK中的日志,那么如何才能让SDK输出更多的日志呢?问题解答 方法就是引入 Logging SDK,然后再初始化 ServiceBusClient 对象时,设置logging_enabl…

nvm和nodejs安装

nvm和nodejs安装安装 nvm 全名 node.js version management,顾名思义是一个nodejs的版本管理工具。通过它可以安装和切换不同版本的nodejs。首先下载安装包,可以用GitHub上的,可以有点看,也可以用一些镜像,然后点击安装一直下一步即可。 然后打开命令行,可以用nvm -v指令…

dify文件上传到http节点

dify系统上传sys.files变量是Array[File]类型,由于 HTTP 请求节点不支持 Array[File] 上传,需要单独处理每个文件,以下是实现此功能的步骤: 添加迭代节点 迭代输入选sys.files 输出选http请求body http请求body类型选form-data 键值选迭代的item.File 本文使用dify版本为0.…

可行性分析(第五组)

目录 第1章 系统分析 1.1 可行性分析 1.1.1 技术可行性分析 1.1.2 经济可行性分析 1.1.3 社会可行性分析 1.1.4 法律可行性分析 1.2 系统流程分析 1.2.1 系统开发总流程 1.2.2 登录流程 1.2.3 系统操作流程 1.2.4 系统性能分析 第1章 可行性分析 1.1可行性分析 下面分别从技术可…

C# 子窗体中调用父窗体中的方法(或多窗体之间方法调用)

看似一个简单的功能需求,其实很多初学者处理不好的,很多朋友会这么写:C# Code://父窗体是是frmParent,子窗体是frmChildA //在父窗体中打开子窗体 frmChildA child = new frmChildA(); child.MdiParent = this; child.Show();//子窗体调父窗体方法: //错误的调用!!!!!!!! …

Qt HTTP模块——调用API对话DeepSeek

HTTP模块 Qt的网络模块(QtNetwork)支持HTTP/HTTPS协议,提供异步、非阻塞的API,实现客户端与服务器之间的 HTTP 请求与响应交互。核心类:QNetworkAccessManager:负责协调网络操作(如GET/POST请求),管理请求队列和返回的响应。 QNetworkRequest:封装HTTP请求的详细信息…

Linux下环境变量

Linux打印环境变量: echo $PATH

GitLearning

创建新仓库 创建新文件夹,打开,然后执行 git init创建新的 git 仓库 (也可以直接 git clone 远程仓库) git clone /path/to/repository git clone username@host:/path/to/repository工作流 本地仓库由 git 维护的三棵树。第一个是工作目录,它持有实际文件;第二个是暂存区(…

ai理解需求生成测试用例-deepseek

ai:把用例捡起来!把用例捡起来! 给大模型需求文档,让它完成设计用例,编写用例,包括功能用例、接口用例、自动化测试用例,自执行~最后发送至工作群中 直接使用deepseek即可 执行一下看看: 调用ds分析需求: 生成功能/接口用例: 生成自动化用例: 看一下自动生成的功…

Win下的Cursor连MCP都是坑?

最近研究了下MCP(模型上下文协议),MCP是由Anthropic(Claude母公司)在2024年11月25日提出并开源的,算是一个比较新的东西。 目前已经被Claude桌面端、Cline、Continue、Cursor、Windsurf等工具应用。在Windows上还是有蛮多坑的,Mac调用会相对简单很多。先来说下MCP能做什…

足球滚球大小球技巧:现代化AI技术 基于机器学习的高精度预测模型与动态策略优化

​ 在足球竞技赛事领域,滚球大小球策略因其动态性和实时性成为复杂数据分析与机器学习(ML)技术的前沿应用场景。本文旨在通过系统化的数据建模与算法优化,探讨如何构建高精度的进球数预测模型,并制定动态调整的策略。文章将从数据预处理、特征工程、模型构建、实时预测优化…