进程间通讯
渲染进程=>主进程=>渲染进程
异步
在渲染器进程 (网页) 中
1 2 3 4 5 6 7
| const { ipcRenderer } = require("electron");
ipcRenderer.send('downloadfile', 'http://www.psvmc.cn/favicon.ico') ipcRenderer.on('downloadfile-result', (event, arg) => { console.log(arg) })
|
在主进程中
1 2 3 4 5 6
| const { ipcMain } = require("electron")
ipcMain.on("downloadfile", (event, arg) => { console.log(arg); event.reply("downloadfile-result", "E://1.jpg"); });
|
回复可以用
1
| event.sender.send('downloadfile-result', '主进程已收到消息');
|
注意
event.reply可以看作event.sender.send的便捷用法。
同步
在渲染器进程 (网页) 中
1 2
| const { ipcRenderer } = require('electron') console.log(ipcRenderer.sendSync('synchronous-message', 'ping'))
|
在主进程中
1 2 3 4 5
| const { ipcMain } = require('electron') ipcMain.on('synchronous-message', (event, arg) => { console.log(arg) event.returnValue = 'pong' })
|
主进程=>渲染进程
在主进程中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const { ipcMain } = require('electron')
let rtmpWindow = new BrowserWindow({ width: 400, height: 600, frame: false, hasShadow: false, show: false, title: "", webPreferences: { nodeIntegration: true } }); rtmpWindow.loadFile("views/rtmp.html"); rtmpWindow.webContents.openDevTools()
rtmpWindow.webContents.on('did-finish-load', () => { rtmpWindow.webContents.send('asynchronous-msg',"test"); })
|
在渲染器进程 (网页) 中
1 2 3 4 5
| const { ipcRenderer } = require('electron')
ipcRenderer.on('asynchronous-msg', (event, arg) => { console.log(arg) })
|
渲染进程=>渲染进程
使用全局共享属性
使用全局共享属性或者用 Storage API( localStorage,sessionStorage 或者 IndexedDB)。
但不具备事件机制,没有实质的通信功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| global.saveDefault= { token: 'default value', name: 'default value', password: 'default value', }
require('electron').remote.getGlobal('saveDefault').token= 'token' require('electron').remote.getGlobal('saveDefault').name= 'name'
console.log(require('electron').remote.getGlobal('saveDefault').name) console.log(require('electron').remote.getGlobal('saveDefault').token) console.log(require('electron').remote.getGlobal('saveDefault').password)
|
利用主进程做消息中转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ipcRenderer.send('ping-event', 'ping');
ipcMain.on( 'ping-event', (event, arg) => { yourWindow.webContents.send('pong-event', 'something'); } );
ipcRenderer.on( 'pong-event', (event, arg) => { } );
|
使用 ipcRenderer.sendTo()
1 2 3
| ipcRenderer.sendTo(webContentsId, channel, [, arg1][, arg2][, ...]); ipcRenderer.sendTo(winId, 'ping', 'someThing');
|
页面数据共享
渲染进程之间
在两个网页(渲染进程)间共享数据最简单的方法是使用浏览器中已经实现的 HTML5 API。
其中比较好的方案是用 Storage API( localStorage,sessionStorage 或者 IndexedDB)。
所有进程间
但是如果要想在主进程和渲染进程之间共享数据,就不能用上面所说的方式了。
可以用 IPC 机制或global.sharedObject
IPC
用 Electron 内的 IPC 机制实现。
global.sharedObject
将数据存在主进程的某个全局变量中,然后在多个渲染进程中使用 remote 模块来访问它。
在主进程中
1 2 3
| global.sharedObject = { username: 'default value' }
|
在第一个页面中
1 2
| const remote = window.require("electron").remote; remote.getGlobal('sharedObject').username = '000';
|
在第二个页面中
1 2
| const remote = window.require("electron").remote; console.log(remote.getGlobal('sharedObject').username);
|
主进程
1 2 3
| global.sharedObject.username = "123";
console.info(global.sharedObject.username);
|
invoke和send
ipcRenderer.invoke 和 ipcRenderer.send 是 Electron 中渲染进程(通常是网页)与主进程进行通信时使用的两个方法。
区别
它们存在以下区别:
通信模式
ipcRenderer.send:采用的是单向通信模式。当在渲染进程中调用 ipcRenderer.send 方法时,它会向主进程发送一个异步消息。
渲染进程在发送消息后不会等待主进程的响应,而是会继续执行后续的代码,也就是说渲染进程不会主动获取主进程针对该消息的处理结果。
ipcRenderer.invoke:属于双向通信模式。它会向主进程发送一个消息,并且会暂停当前代码的执行,等待主进程返回响应结果。
只有当主进程处理完消息并返回结果后,渲染进程才会继续执行后续代码,所以可以直接获取主进程的处理结果。
返回值
- ipcRenderer.send:该方法没有返回值。因为它只是单纯地发送消息,不关心主进程的响应情况,所以不会返回任何数据给调用者。
- ipcRenderer.invoke:会返回一个 Promise 对象。这个 Promise 对象在主进程成功处理消息并返回结果时会被 resolve,如果在通信过程中出现错误,Promise 则会被 reject。因此,可以使用
async/await 或者 .then() 和 .catch() 方法来处理主进程返回的结果或错误。
主进程处理方式
ipcRenderer.send:主进程需要使用 ipcMain.on 来监听渲染进程发送的消息。
在事件处理函数中,主进程可以对消息进行处理,但如果需要向渲染进程发送响应,还需要额外调用 event.sender.send 方法。
ipcRenderer.invoke:主进程需要使用 ipcMain.handle 来处理渲染进程的消息。
ipcMain.handle 的处理函数可以返回一个值或者 Promise,返回的值会作为响应发送给渲染进程。
代码示例
ipcRenderer.send/ipcMain.on
渲染进程代码
1 2 3
| const { ipcRenderer } = require('electron');
ipcRenderer.send('message-to-main', '这是一条来自渲染进程的消息');
|
主进程代码
1 2 3 4 5 6
| const { ipcMain } = require('electron'); ipcMain.on('message-to-main', (event, arg) => { console.log(arg); event.sender.send('reply-to-renderer', '主进程已收到消息'); });
|
ipcRenderer.invoke/ipcMain.handle
渲染进程代码
1 2 3 4 5 6 7 8 9 10
| const { ipcRenderer } = require('electron'); async function getMainProcessResponse() { try { const response = await ipcRenderer.invoke('request-to-main', '请求数据'); console.log(response); } catch (error) { console.error(error); } } getMainProcessResponse();
|
主进程代码
1 2 3 4 5
| const { ipcMain } = require('electron'); ipcMain.handle('request-to-main', (event, arg) => { console.log(arg); return '这是主进程的响应'; });
|
使用场景
- ipcRenderer.send:适用于那些不需要从主进程获取反馈的场景,例如通知主进程执行某个操作(如打开一个新窗口、记录日志等)。
- ipcRenderer.invoke:适用于需要从主进程获取数据或结果的场景,比如请求主进程读取文件内容、进行数据库查询等。
获取版本号
主进程直接更新
1 2 3 4 5
| mainWindow.on("ready-to-show", () => { const appVersion = app.getVersion(); let title = "抓取工具 v" + appVersion; mainWindow.setTitle(title); });
|
渲染进程获取更新
主进程
1 2 3 4 5 6 7
| import { app, ipcMain } from "electron";
ipcMain.handle("getAppVersion", async () => { const appVersion = app.getVersion(); console.log("当前版本号:", appVersion); return appVersion; });
|
渲染进程
1 2 3 4 5 6 7 8 9
| async getAppVersionAction() { try { let appVersion = await ipcRenderer.invoke("getAppVersion"); console.log("当前版本号:", appVersion); document.title = "抓取工具 v" + appVersion; } catch (error) { console.log("获取版本号失败:", error); } },
|
versionCode获取
在 Electron 中,没有原生的 versionCode 概念。
我们可以在 package.json 中手动添加 versionCode 字段(Electron 不会自动生成,需自己维护),再通过引用方式读取该字段。
1 2 3 4 5
| const packageJson = require('./package.json'); const versionCode = packageJson.versionCode;
console.log('versionCode:', versionCode);
|