电子预加载与电子主

  • 本文关键字:加载 node.js electron
  • 更新时间 :
  • 英文 :


我目前正在使用Electron v16。显然,在这个版本上,我们不能再在渲染器线程中使用内置的Node模块了。

唯一的方法是使用electron-preload.js.

我已阅读此资源:https://whoisryosuke.com/blog/2022/using-nodejs-apis-in-electron-with-react/其中作者利用ipcMain将实现代码放置在electron-main.js中,并通过electron-preload.jscontextBridge调用该代码。

我的问题是:如果将整个实现代码放在electron-preload中,而不需要从这里调用事件并将其发送到ipcMain?,会有什么不同吗

这是作者的代码:

电子预载.js:

const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
blenderVersion: async (blenderPath) =>
ipcRenderer.invoke('blender:version', blenderPath),
},
});

electronic-main.js

ipcMain.handle('blender:version', async (_, args) => {
console.log('running cli', _, args)
let result
if (args) {
const blenderExecutable = checkMacBlender(args)
// If MacOS, we need to change path to make executable
const checkVersionCommand = `${blenderExecutable} -v`
result = execSync(checkVersionCommand).toString()
}
return result
})

我在想,这样做是否可以接受(赞成/反对?(:

电子预载.js:

const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('electron', {
blenderVersion: async (blenderPath) =>
console.log('running cli', _, args)
let result
if (args) {
const blenderExecutable = checkMacBlender(args)
// If MacOS, we need to change path to make executable
const checkVersionCommand = `${blenderExecutable} -v`
result = execSync(checkVersionCommand).toString()
}
return result
},
});

从功能上讲,两者根本没有区别。。。

也就是说,将代码的调用与其实现分离在";分离你的顾虑";。如果从render到main有很多调用(反之亦然(,那么preload.js脚本将变得巨大。试图管理这样大小的文件,尤其是当您在Electron应用程序中使用typescript时,可能会出现问题。此类声明的开销将越来越大。

我采取了一种更彻底但在我看来更简单的方法。。。

我使用CCD_ 6脚本纯粹作为声明";频道名称";并通过使用纯IPC句柄来实现主进程和渲染进程之间的通信,例如:

  • ipcRenderer.send(通道,…args(

  • ipcRenderer.on(通道,侦听器(

  • ipcRenderer.ioke(通道,…args(.

拥有这样一个简单的脚本意味着我只需要管理一个preload.js脚本,并且我在创建的每个窗口中都使用它。


这是我的preload.js脚本。要使用,只需添加您的";频道名称";例如:在这个例子中,我添加了通道名称test:channel,只在主到渲染进程之间使用。

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': [
'test:channel' // Our 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);
}
}
}
);

在我的main.js脚本中,只有IPC和通道名称以及任何相关数据(如果有的话(。

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.webContents.send('test:channel', 'Hello from the main thread') })
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
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>Electron Test</title>
</head>
<body>
<div id="test"></div> // Outputs "Hello from the main thread"
</body>
<script>
window.ipcRender.receive('test:channel', (text) => {
document.getElementById('test').innerText = text;
})
</script>
</html>

要在主进程和渲染进程中使用此preload.jsscipt,将应用以下实现。

/**
* Render --> Main
* ---------------
* Render:  window.ipcRender.send('channel', data); // Data is optional.
* Main:    electronIpcMain.on('channel', (event, data) => { methodName(data); })
*
* Main --> Render
* ---------------
* Main:    windowName.webContents.send('channel', data); // Data is optional.
* Render:  window.ipcRender.receive('channel', (data) => { methodName(data); });
*
* Render --> Main (Value) --> Render
* ----------------------------------
* Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main:    electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
*
* Render --> Main (Promise) --> Render
* ------------------------------------
* Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main:    electronIpcMain.handle('channel', async (event, data) => {
*              return await promiseName(data)
*                  .then(() => { return result; })
*          });
*/

请记住,不能返回无法使用结构化克隆算法序列化的函数、promise或其他对象。有关详细信息,请参阅对象序列化。

最新更新