Dev*_*yan 6 html javascript node.js electron
我正在学习使用 Electron,在尝试让我的应用程序与前端通信时,我知道我需要使用 来ipcRenderer获取对 DOM 元素的引用,然后将该信息传递给ipcMain.
我尝试遵循此处和此处建议的大部分建议,但是这两个示例都使用了require('electron').ipcMain,并且每当我尝试将与前端交互的脚本包含到我的 HTML 中时,自Uncaught ReferenceError: require is not defined. 我已经搜索了几个小时,但没有找到解决方案 - 很明显我做错了什么。
我的main.js很简单,我只是创建我的窗口,然后创建一个 ipc 侦听器,如下所示:
const { app, BrowserWindow } = require("electron");
const ipc = require('electron').ipcMain;
function createWindow() {
const window = new BrowserWindow({
transparent: true,
frame: false,
resizable: false,
center: true,
width: 410,
height: 550,
});
window.loadFile("index.html");
}
app.whenReady().then(createWindow);
ipc.on('invokeAction', (event, data) => {
var result = "test result!";
event.sender.send('actionReply', result);
})
Run Code Online (Sandbox Code Playgroud)
在我希望用来操作 DOM 的文件中,我尝试获取元素 ID,然后添加一个事件侦听器,如下所示:
const ipc = require('electron').ipcRenderer;
const helper = require("./api");
var authenticate_button = ipcRenderer.getElementById("authenticate-button");
var authButton = document.getElementById("authenticate-button");
authButton.addEventListener("click", () => {
ipc.once('actionReply', (event, response) => {
console.log("Hello world!");
})
ipc.send('invokeAction');
});
function onAuthenticateClick() {
helper.authenticateLogin(api_public, api_secret, access_public, access_secret);
}
Run Code Online (Sandbox Code Playgroud)
最后,我的 HTML 仅包含一个我希望将事件侦听器附加到的按钮:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Project Test</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="main-container">
<button id="authenticate-button" type="submit" onclick="">Authenticate</button>
<p id="status-label">Not Authenticated</p>
</div>
<script src="script.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
如果有人可以帮助我指明如何使这个基本功能发挥作用的正确方向,那将非常有帮助!
正如 AlekseyHoffman 所提到的,您无法ipcRenderer在前端 js 文件中访问的原因是您nodeIntegration设置为 false。也就是说,现在默认将其设置为 false 是有原因的;它使您的应用程序安全性大大降低。
让我建议一种替代方法:不要尝试ipcRenderer通过设置为 true 来直接从前端 js访问nodeIntegration,而是从 preload.js 访问它。在 preload.js 中,您可以有选择地公开要在前端访问的 ipcMain 函数(来自 main.js 文件)(包括那些可以从 main.js 发送回数据的函数),并通过ipcRenderer那里调用它们。在前端 js 中,您可以访问公开这些函数的 preload.js 对象;然后 preload.js 将调用这些 main.js 函数ipcRenderer,并将数据返回给调用它的前端 js。
这是一个简单但完全有效的示例(这些文件应该足以构建一个在 main.js 和前端之间进行双向通信的电子应用程序。在此示例中,以下所有文件都位于同一目录中。):
main.js
// boilerplate code for electron..
const {
app,
BrowserWindow,
ipcMain,
contextBridge
} = require("electron");
const path = require("path");
let win;
/**
* make the electron window, and make preload.js accessible to the js
* running inside it (this will allow you to communicate with main.js
* from the frontend).
*/
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,
preload: path.join(__dirname, "./preload.js") // path to your preload.js file
}
});
// Load app
win.loadFile(path.join(__dirname, "index.html"));
}
app.on("ready", createWindow);
// end boilerplate code... now on to your stuff
/**
* FUNCTION YOU WANT ACCESS TO ON THE FRONTEND
*/
ipcMain.handle('myfunc', async (event, arg) => {
return new Promise(function(resolve, reject) {
// do stuff
if (true) {
resolve("this worked!");
} else {
reject("this didn't work!");
}
});
});
Run Code Online (Sandbox Code Playgroud)
请注意,我使用的示例是ipcMain.handle因为它允许双向通信并返回 Promise 对象 - 即,当您通过 preload.js 从前端访问此函数时,您可以获取 Promise 以及其中的数据。
预加载.js:
// boilerplate code for electron...
const {
contextBridge,
ipcRenderer
} = require("electron");
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
// end boilerplate code, on to your stuff..
/**
* HERE YOU WILL EXPOSE YOUR 'myfunc' FROM main.js
* TO THE FRONTEND.
* (remember in main.js, you're putting preload.js
* in the electron window? your frontend js will be able
* to access this stuff as a result.
*/
contextBridge.exposeInMainWorld(
"api", {
invoke: (channel, data) => {
let validChannels = ["myfunc"]; // list of ipcMain.handle channels you want access in frontend to
if (validChannels.includes(channel)) {
// ipcRenderer.invoke accesses ipcMain.handle channels like 'myfunc'
// make sure to include this return statement or you won't get your Promise back
return ipcRenderer.invoke(channel, data);
}
},
}
);
Run Code Online (Sandbox Code Playgroud)
渲染器进程(即你的前端 js 文件 - 我将其称为 frontend.js):
// call your main.js function here
console.log("I'm going to call main.js's 'myfunc'");
window.api.invoke('myfunc', [1,2,3])
.then(function(res) {
console.log(res); // will print "This worked!" to the browser console
})
.catch(function(err) {
console.error(err); // will print "This didn't work!" to the browser console.
});
Run Code Online (Sandbox Code Playgroud)
索引.html
<!DOCTYPE html>
<html>
<head>
<title>My Electron App</title>
</head>
<body>
<h1>Hello Beautiful World</h1>
<script src="frontend.js"></script> <!-- load your frontend script -->
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
包.json
{
"name": "myapp",
"main": "main.js",
"scripts": {
"start": "electron ."
}
}
Run Code Online (Sandbox Code Playgroud)
上面的文件应该足以拥有一个完全运行的电子应用程序,并在 main.js 和前端 js 之间进行通信。将它们全部放在一个目录中,名称main.js为preload.js、 、frontend.js、 和index.html,package.json并使用 启动您的电子应用程序npm start。请注意,在本示例中,我将所有文件存储在同一目录中;确保将这些路径更改为它们存储在系统上的任何位置。
请参阅以下链接以获取更多信息和示例:
概述为什么需要IPC以及将nodeintegration设置为true的安全问题