当前位置:网站首页>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
边栏推荐
- Go modules daily use
- MySQL数据库 - 单表查询(一)
- Machine learning catalog
- 点云数据集常用处理
- Using oes texture + glsurfaceview + JNI to realize player picture processing based on OpenGL es
- MFC obtains local IP (used more in network communication)
- RuntimeError: Providing a bool or integral fill value without setting the optional `dtype` or `out`
- 【数值预测案例】(3) LSTM 时间序列电量预测,附Tensorflow完整代码
- MySQL syntax collation (4)
- Pdf reference learning notes
猜你喜欢
精简CUDA教程——CUDA Driver API
深度分析数据恢复原理——那些数据可以恢复那些不可以数据恢复软件
山东大学软件学院项目实训-创新实训-网络安全靶场实验平台(七)
Reflection on the performance of some OpenGL operations in the past
MySQL syntax collation (2)
Garbage collector and memory allocation strategy
Virtual machine performance monitoring and fault handling tools
Command - sudo
Matlab 2019 installation of deep learning toolbox model for googlenet network
[webrtc] add x264 encoder for CEF / Chromium
随机推荐
[report] Microsoft: application of deep learning methods in speech enhancement
Is meituan, a profit-making company with zero foundation, hungry? Coupon CPS applet (with source code)
Kubernetes入门到精通-KtConnect(全称Kubernetes Toolkit Connect)是一款基于Kubernetes环境用于提高本地测试联调效率的小工具。
考试系统进入试卷优化思路
C6748 软件仿真和硬件测试 ---附详细FFT硬件测量时间
Strange passion
优先使用组合而不使用继承
深度学习环境搭建步骤—gpu
MySQL数据库 - 数据库和表的基本操作(二)
IIS数据转换问题16bit转24bit
Some speculation about the decline of adults' language learning ability
[report] Microsoft: application of deep learning methods in speech enhancement
Using oes texture + glsurfaceview + JNI to realize player picture processing based on OpenGL es
Zero cost, zero foundation, build profitable film and television applet
@Mapperscan and @ mapper
Speex维纳滤波与超几何分布的改写
Build intelligent garbage classification applet based on Zero
antd dropdown + modal + textarea导致的textarea光标不可被键盘控制问题
Core concepts of rest
Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies