P粉6158297422023-08-28 19:14:20
并非官方文档中的所有内容都可以在代码中的任何位置直接实现。您必须需要对环境和流程有简明的了解。
环境/流程 | 描述 |
---|---|
主要 | API 更接近操作系统(低级)。其中包括文件系统、基于操作系统的通知弹出窗口、任务栏等。这些是通过 Electron 的核心 API 和 Node.js 的组合实现的 |
预加载 | 最近的附录,以防止主环境中可用的强大 API 泄漏。有关更多详细信息,请参阅 Electron v12 变更日志< /a> 和问题#23506。 |
渲染器 | 现代 Web 浏览器的 API,例如 DOM 和前端 JavaScript(高级)。这是通过 Chromium 实现的。 |
场景 | contextIsolation |
nodeIntegration |
备注 |
---|---|---|---|
A | 假 |
假 |
不需要预加载。 Node.js 在 Main 中可用,但在 Renderer 中不可用。 |
B | 假 |
true |
不需要预加载。 Node.js 在 Main 和 Renderer 中可用。 |
C | true |
假 |
需要预加载。 Node.js 在主加载和预加载中可用,但在渲染器中不可用。 默认。 推荐。 |
D | true |
true |
需要预加载。 Node.js 在 Main、Preload 和 Renderer 中可用。 |
您必须使用 Electron 的进程间通信 (IPC) 才能使主进程和渲染进程进行通信。
BrowserWindow.webContents.send() code>
向渲染器发送消息的方法ipcMain.handle()
从渲染器接收消息的方法主要
/** * Sending messages to Renderer * `window` is an object which is an instance of `BrowserWindow` * `data` can be a boolean, number, string, object, or array */ window.webContents.send( 'custom-endpoint', data ); /** * Receiving messages from Renderer */ ipcMain.handle( 'custom-endpoint', async ( event, data ) => { console.log( data ) } )
预加载
const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld( 'api', { send: ( channel, data ) => ipcRenderer.invoke( channel, data ), handle: ( channel, callable, event, data ) => ipcRenderer.on( channel, callable( event, data ) ) } )
渲染器
/** * Sending messages to Main * `data` can be a boolean, number, string, object, or array */ api.send( 'custom-endpoint', data ) /** * Receiving messages from Main */ api.handle( 'custom-endpoint', ( event, data ) => function( event, data ) { console.log( data ) }, event);
尽可能遵守对相同流程/环境的承诺。您在 main 上的承诺应该保留在 main 上。您对渲染器的承诺也应该保留在渲染器上。不要做出从主程序跳转到预加载程序再到渲染器的承诺。
您的大部分业务逻辑仍应位于主端或渲染器端,但绝不应位于预加载中。这是因为预载几乎只是作为一种媒介而存在。预载应该非常小。
在OP的情况下, fs
应该在主端实现。
P粉3233748782023-08-28 11:18:17
我已经发表了一篇关于 Electron 历史的较大文章( Electron 版本中的安全性如何发生变化)以及 Electron 开发人员可以采取的其他安全注意事项,以确保在新应用程序中正确使用预加载文件。
正如另一位用户所问,让我在下面解释我的答案。
在 Electron 中使用 preload.js
的正确方法是在您的应用可能需要 require
的任何模块周围公开白名单包装器。
从安全角度来看,公开 require
或通过 preload.js
中的 require
调用检索的任何内容都是危险的(请参阅< a href="https://github.com/electron/ Electron/issues/9920#issuecomment-575839738" rel="noreferrer">我的评论以获取更多解释原因)。如果您的应用程序加载远程内容(许多应用程序都会这样做),则尤其如此。
为了正确执行操作,您需要在 < 上启用许多选项code>BrowserWindow 正如我在下面详细介绍的。设置这些选项会强制您的电子应用程序通过 IPC(进程间通信)进行通信,并将两个环境相互隔离。像这样设置您的应用程序可以让您验证后端中可能是 require
模块的任何内容,而客户端不会对其进行篡改。
下面,您将找到一个简短的示例,介绍我所讨论的内容以及它在您的应用中的外观。如果您是新手,我可能建议使用 secure-electron-template
< /a> (我是其作者)在构建电子应用程序时从一开始就融入了所有这些安全最佳实践。
此页面也有很好的信息使用 preload.js 制作安全应用程序时所需的架构。
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
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) => { // whitelist channels let validChannels = ["toMain"]; if (validChannels.includes(channel)) { ipcRenderer.send(channel, data); } }, receive: (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.receive("fromMain", (data) => { console.log(`Received ${data} from main process`); }); window.api.send("toMain", "some data"); </script> </body> </html>