将消息从 Main 发送到 Renderer
Sending message from Main to Renderer
有人可以帮助我吗?我对如何解决这个问题完全感到困惑。我现在已经花了大约一个星期的时间试图为此找到解决方案,但结果很短,而且似乎在线上缺乏可靠的解决方案。我创建了一个 github 存储库来演示这个问题。
简而言之,我在我的应用程序中实现了一个状态栏,我想在其中填充各种字符串消息。这些消息将从导入电子的 js 文件中包含的函数发送,这意味着它不能直接访问渲染器。那么我将如何将这些消息发送到渲染器。我假设这需要使用 ContextBridge 来完成,但我不知道如何成功地做到这一点,所以如果你的回复只是将我链接到上下文桥文档,请不要打扰,大声笑我已经筋疲力尽了在那。我正在考虑的另一种选择是使用自定义事件,但我不确定这是否也能解决问题。
这是我在 github 上尝试与 repo 一起做的示例。如果您提出拉取请求来修复回购协议,我很乐意合并并保留回购协议 public 以供其他人从中受益并与社区分享。 https://github.com/JokerMartini/statusbar
作为一个小问题,我不确定为什么我无法再从未加载到渲染线程的 js 文件中从 'app' 调用 getPath。
我从 Renderer 触发了一个方法
index.vue
const doWork = () => {
window.messenger.doWork();
}
电子-preload.js
import { contextBridge } from "electron";
const messenger = require("../src/helpers/messenger");
contextBridge.exposeInMainWorld("messenger", messenger);
messenger.js
const { app } = require("electron");
const path = require("path");
// using electron module to demonstrate this file can't be imported into renderer
export function showMessage(msg) {
const dir = path.join(app.getPath("documents"), "presets");
console.log(dir);
// TODO: send message to renderer...
}
export function doWork() {
console.log("Doing working...");
// step 1: long process
showMessage("Processing step 1...");
// step 2: long process
showMessage("Processing step 2...");
// step 3: long process
showMessage("Processing step 3...");
}
我想显示从 main 发送到 renderer 的消息显示在
的状态栏中
main.vue
<q-footer>
<q-bar>
<span class="text-caption">Show message here...</span>
</q-bar>
</q-footer>
** 更新 01 **
由于某种原因,渲染器没有收到我的消息。这是我的代码更改
电子-preload.js
import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("electronAPI", {
setStatus: (callback, func) =>
ipcRenderer.on("set-status", (event, ...args) => func(...args)),
});
index.vue
<template>
<q-page class="flex flex-center">
<q-btn label="Show Message" @click="doWork" />
</q-page>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
setup() {
// send message for testing...
const doWork = () => {
window.electronAPI.setStatus("sfsfsdfsd");
};
// recieve status messages...
window.electronAPI.setStatus("set-status", (data) => {
console.log("STATUS:", data);
// TODO $store.dispatch("....");
});
return {
doWork,
};
},
});
</script>
您想使用 ipcMain
(在主进程中)和 ipcRenderer
(在渲染进程中)。如果您将脚本与 https://www.electronjs.org/docs/latest/tutorial/ipc 中的示例进行比较,那就是缺少的内容。
(有个section specifically on doing main to renderer.)
这确实更清晰、更简单,但更容易被滥用。所以最好忽略任何超过一年左右的在线教程。 (虽然 contextBridge
很新,所以如果他们提到它,那么他们应该是最近的。)
对我有用的技术是 而不是 使用 preload.js
脚本来定义具体实现。相反,我使用 preload.js
脚本来仅定义我可以在主线程和渲染线程之间进行通信的通道(名称)。 IE:分离你的顾虑。在主线程脚本和呈现线程脚本中实现具体功能。
preload.js
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [
'message:update' // Here is your channel name
],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Note: Though I do not use Vue.js, you should get the gist of the below two files.
main.js
(主线程)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
// Send a message to the window.
window.webContents.send('message:update', 'Doing work...');
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
index.html
(渲染线程)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span id="text-caption">Show message here...</span>
</body>
<script>
// Listen for message updates from the main thread.
window.ipcRender.receive('message:update', (message) => {
document.getElementById('text-caption').innerText = message;
});
</script>
</html>
有人可以帮助我吗?我对如何解决这个问题完全感到困惑。我现在已经花了大约一个星期的时间试图为此找到解决方案,但结果很短,而且似乎在线上缺乏可靠的解决方案。我创建了一个 github 存储库来演示这个问题。
简而言之,我在我的应用程序中实现了一个状态栏,我想在其中填充各种字符串消息。这些消息将从导入电子的 js 文件中包含的函数发送,这意味着它不能直接访问渲染器。那么我将如何将这些消息发送到渲染器。我假设这需要使用 ContextBridge 来完成,但我不知道如何成功地做到这一点,所以如果你的回复只是将我链接到上下文桥文档,请不要打扰,大声笑我已经筋疲力尽了在那。我正在考虑的另一种选择是使用自定义事件,但我不确定这是否也能解决问题。
这是我在 github 上尝试与 repo 一起做的示例。如果您提出拉取请求来修复回购协议,我很乐意合并并保留回购协议 public 以供其他人从中受益并与社区分享。 https://github.com/JokerMartini/statusbar
作为一个小问题,我不确定为什么我无法再从未加载到渲染线程的 js 文件中从 'app' 调用 getPath。
我从 Renderer 触发了一个方法
index.vue
const doWork = () => {
window.messenger.doWork();
}
电子-preload.js
import { contextBridge } from "electron";
const messenger = require("../src/helpers/messenger");
contextBridge.exposeInMainWorld("messenger", messenger);
messenger.js
const { app } = require("electron");
const path = require("path");
// using electron module to demonstrate this file can't be imported into renderer
export function showMessage(msg) {
const dir = path.join(app.getPath("documents"), "presets");
console.log(dir);
// TODO: send message to renderer...
}
export function doWork() {
console.log("Doing working...");
// step 1: long process
showMessage("Processing step 1...");
// step 2: long process
showMessage("Processing step 2...");
// step 3: long process
showMessage("Processing step 3...");
}
我想显示从 main 发送到 renderer 的消息显示在
的状态栏中main.vue
<q-footer>
<q-bar>
<span class="text-caption">Show message here...</span>
</q-bar>
</q-footer>
** 更新 01 **
由于某种原因,渲染器没有收到我的消息。这是我的代码更改
电子-preload.js
import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("electronAPI", {
setStatus: (callback, func) =>
ipcRenderer.on("set-status", (event, ...args) => func(...args)),
});
index.vue
<template>
<q-page class="flex flex-center">
<q-btn label="Show Message" @click="doWork" />
</q-page>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
setup() {
// send message for testing...
const doWork = () => {
window.electronAPI.setStatus("sfsfsdfsd");
};
// recieve status messages...
window.electronAPI.setStatus("set-status", (data) => {
console.log("STATUS:", data);
// TODO $store.dispatch("....");
});
return {
doWork,
};
},
});
</script>
您想使用 ipcMain
(在主进程中)和 ipcRenderer
(在渲染进程中)。如果您将脚本与 https://www.electronjs.org/docs/latest/tutorial/ipc 中的示例进行比较,那就是缺少的内容。
(有个section specifically on doing main to renderer.)
这确实更清晰、更简单,但更容易被滥用。所以最好忽略任何超过一年左右的在线教程。 (虽然 contextBridge
很新,所以如果他们提到它,那么他们应该是最近的。)
对我有用的技术是 而不是 使用 preload.js
脚本来定义具体实现。相反,我使用 preload.js
脚本来仅定义我可以在主线程和渲染线程之间进行通信的通道(名称)。 IE:分离你的顾虑。在主线程脚本和呈现线程脚本中实现具体功能。
preload.js
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [
'message:update' // Here is your channel name
],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Note: Though I do not use Vue.js, you should get the gist of the below two files.
main.js
(主线程)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
// Send a message to the window.
window.webContents.send('message:update', 'Doing work...');
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
index.html
(渲染线程)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span id="text-caption">Show message here...</span>
</body>
<script>
// Listen for message updates from the main thread.
window.ipcRender.receive('message:update', (message) => {
document.getElementById('text-caption').innerText = message;
});
</script>
</html>