【发布时间】:2017-10-15 23:04:05
【问题描述】:
使用电子、反应 (es6 / jsx)、sass、pouchdb 和 webpack 2 设置。我无法导入或要求 ipcRenderer 使主进程和渲染器进程之间的通信成为可能。我的设置可以在这里找到:https://github.com/wende60/timeTracker
任何提示如何将 ipcRenderer 放入反应组件?
干杯,乔
【问题讨论】:
使用电子、反应 (es6 / jsx)、sass、pouchdb 和 webpack 2 设置。我无法导入或要求 ipcRenderer 使主进程和渲染器进程之间的通信成为可能。我的设置可以在这里找到:https://github.com/wende60/timeTracker
任何提示如何将 ipcRenderer 放入反应组件?
干杯,乔
【问题讨论】:
const electron = window.require('electron');
const ipcRenderer = electron.ipcRenderer;
我认为这是更好的解决方案,因为它避免了弹出 React 应用程序。
【讨论】:
我遇到了同样的问题。这为我解决了这个问题:
添加webpack.config.js:
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.ExternalsPlugin('commonjs', [
'electron'
])
]
...
}
然后你就可以用它了
import {ipcRenderer} from "electron";
【讨论】:
The externals configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer's (any end-user application) environment. 那么这是否意味着该解决方案仅在用户安装电子的前提下才有效?
我建议你阅读我的回复here。
您需要像这样设置您的应用:
main.js
const {
app,
BrowserWindow,
ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(__dirname, "preload.js") // use a preload script
}
});
// Load app
win.loadFile(path.join(__dirname, "dist/index.html"));
// rest of code..
}
app.on("ready", createWindow);
ipcMain.on("toMain", (event, args) => {
fs.readFile("path/to/file", (error, data) => {
// Do something with file contents
// Send result back to renderer process
win.webContents.send("fromMain", responseObj);
});
});
preload.js
** 更新:不要使用
send键值作为属性名称。它将覆盖win.webContents.send方法,当您尝试在主进程main.js中调用win.webContents.send('your_channel_name')时,它什么也不做。最好使用更好的名称,例如request和response。
const {
contextBridge,
ipcRenderer
} = require("electron");
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
"api", {
//send: (channel, data) => {
request: (channel, data) => {
// whitelist channels
let validChannels = ["toMain"];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
//receive: (channel, func) => {
response: (channel, func) => {
let validChannels = ["fromMain"];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
}
);
index.html
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8"/>
<title>Title</title>
</head>
<body>
<script>
window.api.response("fromMain", (data) => {
console.log(`Received ${data} from main process`);
});
window.api.request("toMain", "some data");
</script>
</body>
</html>
【讨论】:
send 作为 preload.js 的窗口属性时遇到了什么问题?
win.webContents.send('channel', someArgs) 将某个键盘快捷键上的主进程中的事件发送到我的 ipcRendere,但未能在 ipcRendere.on('channel', func()) 中接收数据。花了 1 个小时才发现问题是当您尝试使用 contextBridge.exposeInMainWorld 将 api.send 和 api.receive 方法附加到窗口对象时,它将影响 win.webContents.send 方法并且您将丢失 data arg 内容, 导致你的调用函数不是 webContents.send 方法。
send 更改为request,我现在一切都很好......
截至 2020 年 5 月,我认为Erik Martín Jordán has said it best:
创建一个 preload.js 文件:
window.ipcRenderer = require('electron').ipcRenderer;
在 main.js 上:
// Create the browser window.
mainWindow = new BrowserWindow({
alwaysOnTop: true,
frame: false,
fullscreenable: false,
transparent: true,
titleBarStyle: 'customButtonsOnHover',
show: false,
width: 300,
height: 350,
webPreferences: {
// UPDATE: for electron > V12 consider setting contextIsolation and see: https://github.com/electron/electron/issues/9920#issuecomment-797491175
nodeIntegration: true,
preload: __dirname + '/preload.js'
}
});
// Blur window when close o loses focus
mainWindow.webContents.on('did-finish-load', () => mainWindow.webContents.send('ping', '?') );
此文件上的 mainWindow 变量将预加载 preload.js 文件。现在 React 组件可以调用 window.ipcRenderer 方法了。
在 React app.js 中:
import React, { useEffect, useState } from 'react';
import './App.css';
function App() {
useEffect( () => {
window.ipcRenderer.on('ping', (event, message) => {
console.log(message)
});
}, []);
return (
<div className = 'App'></div>
);
}
export default App;
【讨论】:
我最近一直在研究这个话题,我找到了一个解决方案,可以在电子 main.js 和应用程序的 React 部分之间做一些 ipc。由于两者,import {ipcRenderer} from 'electron'; 在将插件添加到 webpack 模块后和const ipc = require('electron').ipcRenderer; 都产生了一些错误,我最终需要在结果页面中使用电子并将其添加到窗口中。
在index.html我做了这样的事情
<body>
...
<script>
window.ipc = require('electron').ipcRenderer;
</script>
<div id="root">
</div>
...
</body>
在反应index.js 我做了这样的事情:
import React from 'react';
import ReactDOM from 'react-dom';
// ...
if(window.ipc)
ipc.on("some-event", (event, someParameter) => {
ReactDOM.render(
<SomeElement value={someParameter} />,
document.getElementById("root")
);
})
// ...
为了完成这项工作,我从电子应用程序启动了反应页面,在 main.js 我做了类似的事情。
const {app, BrowserWindow} = require("electron"};
const exec = require("child_process").exec;
let main;
app.on("ready", () => {
exec("node start", (err, stdout, stderr) => {
if(err) console.log(err);
console.log("" + stdout);
console.log("" + stderr);
});
main = new BrowserWindow();
main.loadURL("http://localhost:3006");
main.on("close", () => {main = null});
});
因为 electron 在同一个端口上运行,所以我在我的项目中添加了一个 .env 文件,其中包含
PORT=3006
我在我的基础项目中使用了create-react-app my-prj (npm install -g create-react-app) 命令,它看起来像这样
my-prj
|-package.json
|-main.js
|-.env
|-node_modules/
|-public/
|-index.html
|-src/
|-index.js
希望这篇文章对您有所帮助。
【讨论】:
使用webpack-target-electron-renderer 为这个问题找到了一个很好的解决方案,所以我可以在本地主机环境中使用热重载开发 Web 部件。 electron 仅在电子环境中是必需的。
您可以在此处查看一个工作示例: https://github.com/wende60/webpack-web-and-electron-example,来自 acao 的 webpack-web-and-electron-example 并针对 webpack 2 和热替换进行了更新。
如果你对 webpack、electron、react、sass 和 pouchdb 设置感兴趣,请看这里: https://github.com/wende60/timeTracker 工作仍在进行中...
【讨论】:
import { ipcRenderer } from "electron"
import { Component } from "react"
...
class MyComponent extends Component {
render(){
ipcRenderer.send("event", "some data")
return (<div>Some JSX</div>)
}
}
【讨论】: