P粉6158297422023-08-28 19:14:20
Not everything in the official documentation can be implemented directly anywhere in the code. You must need to have a concise understanding of the environment and processes.
Environment/Process | describe |
---|---|
main | API is closer to the operating system (low level). These include the file system, operating system-based notification popups, taskbar, and more. These are implemented through a combination of Electron's core API and Node.js |
Preloading | Recent Appendix to prevent leaks of powerful APIs available in the main environment. See Electron v12 Changelog< /a> and Issue#23506 for more details. |
Renderer | APIs for modern web browsers such as DOM and front-end JavaScript (advanced). This is achieved through Chromium. |
Scenes | contextIsolation |
nodeIntegration |
Remark |
---|---|---|---|
A | Fake |
Fake |
No preloading required. Node.js is available in Main but not in Renderer. |
B | Fake |
true |
No preloading required. Node.js is available in Main and Renderer. |
C | true |
Fake |
Requires preloading. Node.js is available in main load and preload, but not in renderer. default. recommend. |
D | true |
true |
Requires preloading. Node.js is available in Main, Preload and Renderer. |
You must use Electron's inter-process communication (IPC) in order for the main process and the renderer process to communicate.
BrowserWindow.webContents.send() code>
Method to send messages to the rendereripcMain.handle()
Method to receive messages from the renderermain
/** * 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 ) } )
Preloading
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 ) ) } )
Renderer
/** * 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);
Keep commitments to the same process/environment whenever possible. Your promises on main should remain on main. Your commitment to the renderer should also remain on the renderer. Don't make a commitment to jump from the main program to the preloader to the renderer.
Most of your business logic should still be on the main or renderer side, but it should never be in preloading. This is because preloading exists almost exclusively as a medium. The preload should be very small.
In OP's case, fs
should be implemented on the master side.
P粉3233748782023-08-28 11:18:17
I've published a larger article about the history of Electron (how security has changed in Electron versions) and other security considerations that Electron developers can take to ensure that new applications Use preload files correctly in the program.
As another user asked, let me explain my answer below.
The correct way to use preload.js
in Electron is to expose a whitelist wrapper around any modules that your app may require
.
From a security perspective, anything exposed require
or retrieved via a require
call in preload.js
is dangerous (see < a href="https://github.com/electron/ Electron/issues/9920#issuecomment-575839738" rel="noreferrer">my comment for more explanation why). This is especially true if your application loads remote content (which many applications do).
In order to do this correctly, you need to create a new window in < 上启用许多选项code>BrowserWindow as I detail below. Setting these options forces your Electron application to communicate via IPC (Inter-Process Communication) and isolates the two environments from each other. Setting up your application like this allows you to validate anything in the backend that might be a require
module without it being tampered with by the client.
Below, you'll find a short example of what I'm talking about and how it might look in your app. If you are new to this, I might recommend using secure-electron-template
< /a> (I am the author of it) to incorporate all these security best practices from the start when building electron applications .
This page also has good information on the architecture required when using preload.js to make secure applications.
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>