当前位置:网站首页>Electron入门教程3 ——进程通信
Electron入门教程3 ——进程通信
2022-04-23 19:41:00 【害恶细君】
欢迎来到Electron入门教程的第三期教程,这一节非常重要!进程间通信(IPC)是在Electron中构建功能丰富的桌面应用程序的关键部分。因为主进程和渲染进程在Electron的进程模型中有不同的职责,IPC是执行许多常见任务的唯一方式,比如从UI调用本地API或从本地菜单触发web内容的更改。下面就来详细介绍3种常见的通信方式。
* 渲染进程向主进程的单向通信
在Electron中,进程通过开发人员定义的“通道”与ipcMain
模块和ipcRenderer
模块进行通信。这些通道是任意的(您可以任意命名它们)和双向的(您可以为两个模块使用相同的通道名称)。要从渲染进程向主进程发送单向IPC消息,可以再预渲染脚本preload.js里使用ipcRenderer
发送API发送消息,然后在main.js里用ipcMain.on
接收。你通常使用这个模式从你的web内容中调用一个主进程API。我们将通过创建一个简单的应用程序来演示这种模式,该应用程序可以通过编程方式更改窗口的标题。
下面我们用代码来演示一下这个过程,下面是案例的所有代码:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>进程通信</title>
</head>
<body>
Title: <input id="title"/>
<button id="btn" type="button">Set</button>
<script src="./index.js"></script>
</body>
</html>
preload.js
const {
contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
index.js
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
const title = titleInput.value
window.electronAPI.setTitle(title)
});
main.js
const {
app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
运行效果如下(GIF有点慢,别介意):
下面对代码的一些要点进行讲解:
1.在主进程中监听事件
在主进程中,我们使用ipcMain
在set-title
通道上设置一个IPC监听器,这个set-title
是我们在预渲染脚本preload.js里面定义的接口通道。
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
每当消息通过set-title
通道传入时,此函数将找到附加到消息发送者的BrowserWindow
实例,并使用win.setTitle
设置应用窗口的标题。
2.在预加载脚本里面通过定义接口通道
要向上面创建的侦听器发送消息,您可以使用ipcRenderer
。发送API。默认情况下,渲染器进程没有Node.js或Electron模块访问。作为应用程序开发人员,您需要使用contextBridge
从预加载脚本中选择要公开哪些API。此时,您将能够在呈现过程中使用window.electronAPI.setTitle()
函数。
const {
contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
* 渲染进程与主进程的双向通信
双向IPC的一个常见应用是从渲染进程代码中调用主进程模块并等待结果。这可以通过使用ipcRenderer.invoke
来实现,调用ipcMain.handle
配对。在下面的例子中,我们将从渲染进程中打开一个选择本地文件对话框,并返回所选文件的路径。
下面是案例涉及的所有代码:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>进程通信</title>
</head>
<body>
<button type="button" id="btn">Open a File</button>
File path: <strong id="filePath"></strong>
<script src='./index.js'></script>
</body>
</html>
index.js
const btn = document.getElementById('btn')
const filePathElement = document.getElementById('filePath')
btn.addEventListener('click', async () => {
const filePath = await window.electronAPI.openFile()
filePathElement.innerText = filePath
})
main.js
const {
app, BrowserWindow, ipcMain,dialog} = require('electron')
const path = require('path')
async function handleFileOpen() {
const {
canceled, filePaths } = await dialog.showOpenDialog()
if (canceled) {
return ""
} else {
return filePaths[0]
}
}
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
ipcMain.handle('openFileDialog', handleFileOpen)
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
preload.js
const {
contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI',{
openFile: () => ipcRenderer.invoke('openFileDialog')
})
运行效果演示:
下面对代码的一些要点进行讲解:
1.在主进程定义事件处理函数,并监听ICP接口的调用
在主进程中,我们将创建一个调用dialog
模块的showOpenDialog
方法的函数handleFileOpen()
,用于返回用户选择的文件路径的值。在应用准备好之后,里面调用ipcMain.handle()
来监听渲染进程里的ipcRenderer.invoke('openFileDialog')
里定义的openFileDialog
。当index.js里面调用window.electronAPI.openFile()
时,会触发openFileDialog
,进而被主进程监听处理后,返回结果。
2. 调用通过预加载脚本定义接口
在预加载脚本中,我们公开了一个单行openFile
函数,它调用并返回ipcRederer .invoke('openFileDialog')
。
在index.js代码片段中,我们监听对#btn
按钮的点击,并调用window.electronAPI.openFile()
来激活本地的openFile
对话框。然后在#filePath
元素中显示选定的文件路径。
3. ipcRenderer.invoke的替代
ipcRenderer.invoke()
有两种替代方式:
(1)ipcRenderer.send()
:我们所使用的单向通信也可以用来执行双向通信。这是在Electron 7之前通过IPC进行异步双向通信的推荐方式。
preload.js
const {
ipcRenderer } = require('electron')
ipcRenderer.on('asynchronous-reply', (_event, arg) => {
console.log(arg)
// 会打印pong
})
ipcRenderer.send('asynchronous-message', 'ping')
main,js
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg)
//会答应ping
event.reply('asynchronous-reply', 'pong')
})
(1) ipcRenderer.sendSync()
: 这个方法向主进程发送消息,并同步等待响应。
preload.js
const {
ipcRenderer } = require('electron')
const result = ipcRenderer.sendSync('synchronous-message', 'ping')
console.log(result)
// 会打印pong
main.js
const {
ipcMain } = require('electron')
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg)
// 会打印ping
event.returnValue = 'pong'
})
此代码的结构与调用模型非常相似,但出于性能原因,我们建议避免使用此API。它的同步特性意味着它将阻塞呈现程序进程,直到接收到应答。
* 主进程向渲染进程的单向通信
当从主进程向渲染进程发送消息时,您需要指定哪个渲染程序正在接收消息。消息需要通过主进程的WebContents
实例发送到渲染进程。这个WebContents
实例包含一个sent
方法,可以像ipcReender .send
那样使用它。为了演示这个通信模式,将构建一个由菜单栏控制的数字计数器。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>进程通信</title>
</head>
<body>
Current value: <strong id="counter">0</strong>
<script src='./index.js'></script>
</body>
</html>
preload.js
const {
contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
handleCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
index.js
const counter = document.getElementById('counter')
window.electronAPI.handleCounter((event, value) => {
const oldValue = Number(counter.innerText)
const newValue = oldValue + value
counter.innerText = newValue
event.sender.send('counter-value', newValue)
})
main.js
const {
app, BrowserWindow, Menu, ipcMain} = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
click: () => mainWindow.webContents.send('update-counter', 1),
label: 'Increment',
},
{
click: () => mainWindow.webContents.send('update-counter', -1),
label: 'Decrement',
}
]
}
])
Menu.setApplicationMenu(menu)
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
}
app.whenReady().then(() => {
ipcMain.on('counter-value', (_event, value) => {
console.log(value) // will print value to Node console
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
运行效果演示:
对部分代码讲解:
我们首先需要在主流程中使用Electron的Menu
模块构建一个自定义菜单,从主进程向目标渲染器发送IPC消息。单击处理程序通过计数器通道向呈现程序进程发送消息(1或-1)。
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
click: () => mainWindow.webContents.send('update-counter', 1),
label: 'Increment',
},
{
click: () => mainWindow.webContents.send('update-counter', -1),
label: 'Decrement',
}
]
}
])
Menu.setApplicationMenu(menu)
就像前面渲染到主进程的例子一样,我们在预加载脚本preload.js中使用contextBridge
和ipcRederer
模块向渲染进程公开IPC功能:
const {
contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
handleCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
* 渲染进程之间的通信
在Electron中,没有直接的方法在渲染进程之间使用ipcMain
和ipRenderer
模块发送消息,而且这种通信方式其实也非常少用。要做到这一点,你可以使用主进程作为渲染程序之间的消息代理。这将涉及到从一个渲染器向主进程发送消息,主进程将把消息转发给另一个渲染器,这里就不做演示了。
版权声明
本文为[害恶细君]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_50216991/article/details/124240751
边栏推荐
- 考试系统进入试卷优化思路
- Kubernetes getting started to proficient - install openelb on kubernetes
- ESP8266-入门第一篇
- Machine learning catalog
- filebeat、logstash配置安装
- Lpc1768 optimization comparison of delay time and different levels
- How to uninstall easyton
- C学习完结
- C6748 software simulation and hardware test - with detailed FFT hardware measurement time
- HTTP cache - HTTP authoritative guide Chapter VII
猜你喜欢
Garbage collector and memory allocation strategy
精简CUDA教程——CUDA Driver API
MySQL lock
MFCC: Mel频率倒谱系数计算感知频率和实际频率转换
Using oes texture + glsurfaceview + JNI to realize player picture processing based on OpenGL es
MySQL syntax collation (5) -- functions, stored procedures and triggers
山大网安靶场实验平台项目-个人记录(五)
Intuitive understanding of the essence of two-dimensional rotation
Reflection on the performance of some OpenGL operations in the past
Comment créer un pass BEP - 20 sur la chaîne BNB
随机推荐
A simple (redisson based) distributed synchronization tool class encapsulation
RuntimeError: Providing a bool or integral fill value without setting the optional `dtype` or `out`
一个简单的(基于redisson的)分布式同步工具类封装
C学习完结
Is meituan, a profit-making company with zero foundation, hungry? Coupon CPS applet (with source code)
Class loading process of JVM
Unity创建超写实三维场景的一般步骤
MySQL数据库 - 连接查询
MySQL syntax collation
点云数据集常用处理
Use of fluent custom fonts and pictures
【数值预测案例】(3) LSTM 时间序列电量预测,附Tensorflow完整代码
Lottery applet, mother no longer have to worry about who does the dishes (assign tasks), so easy
Speex维纳滤波与超几何分布的改写
MySQL syntax collation (5) -- functions, stored procedures and triggers
An example of using JNI to directly access surface data
C6748 软件仿真和硬件测试 ---附详细FFT硬件测量时间
Application of DCT transform
考试系统进入试卷优化思路
uIP1.0 主动发送的问题理解