Utiliser correctement preload.js dans Electron : un guide complet
P粉197639753
P粉197639753 2023-08-27 20:25:30
0
2
499
<p>J'ai essayé d'utiliser un module de nœud (dans ce cas <code>fs</code>) dans mon processus <code>renderer</code> <pre class="brush:php;toolbar:false;">// main_window.js const fs = exiger('fs') fonction action() { console.log(fs) }</pré> <p><sup>Remarque : lorsque j'appuie sur le bouton dans <code>main_window</code>, la fonction <code>action</code> </sup></p> <p>Mais cela produit l'erreur : </p> <pre class="brush:php;toolbar:false;">Uncaught ReferenceError : require n'est pas défini sur main_window.js:1</pre> <p>J'ai pu résoudre ce problème, comme le suggère cette réponse acceptée, en ajoutant ces lignes à mon <code>main.js</code> lors de l'initialisation de <code>main_window</code> /p> <pre class="brush:php;toolbar:false;">// main.js main_window = nouveau BrowserWindow ({ largeur : 650, hauteur : 550, Préférences Web : { nodeIntegration : vrai } })</pré> Cependant, selon la documentation, ce n'est pas la meilleure pratique, je devrais créer un fichier <code>preload.js</code> et y charger ces modules Node, puis dans tous mes <code>renderer</ code> Utilisez-le dans le processus.Comme ceci :<p><br /></p> <p><code>main.js</code>:</p> <pre class="brush:php;toolbar:false;">main_window = new BrowserWindow({ largeur : 650, hauteur : 550, Préférences Web : { préchargement : path.join(app.getAppPath(), 'preload.js') } })</pré> <p><code>preload.js</code>:</p> <pre class="brush:php;toolbar:false;">const fs = require('fs') window.test = fonction() { console.log(fs) }</pré> <p><code>main_window.js</code>:</p> <pre class="brush:php;toolbar:false;">fonction action() { fenêtre.test() }</pré> <p>Et ça marche ! </p> <heure /> <p>Maintenant, ma question est la suivante : n'est-il pas contre-intuitif que je doive écrire la plupart du code pour le processus <code>renderer</code> n'avez accès qu'au module Node dans <code>preload.js</code>), puis appelez simplement les fonctions dans chaque fichier <code>renderer.js</code> (comme ici, <code>main_window. js</code>) ? Qu'est-ce que je ne comprends pas ici ? </p>
P粉197639753
P粉197639753

répondre à tous(2)
P粉615829742

Considérez cet exemple

Tout ce qui figure dans la documentation officielle ne peut pas être implémenté directement n'importe où dans le code. Vous devez avoir une compréhension concise de l’environnement et des processus.

Environnement/Processus Description
Principal L'API est plus proche du système d'exploitation (niveau bas). Ceux-ci incluent le système de fichiers, les fenêtres contextuelles de notification basées sur le système d'exploitation, la barre des tâches, etc. Ceux-ci sont implémentés via une combinaison de l'API principale d'Electron et de Node.js
Préchargé Récente Annexe pour éviter les fuites de la puissante API disponible dans l'environnement principal. Pour plus de détails, consultez le Journal des modifications d'Electron v12 et le Issue #23506.
Rendu API pour les navigateurs Web modernes tels que DOM et JavaScript frontal (avancé). Ceci est réalisé grâce à Chromium.

Isolement du contexte et intégration des nœuds

Scène contextIsolation nodeIntegration Remarques
A Aucun préchargement requis. Node.js est disponible dans Main mais pas dans Renderer.
B true Aucun préchargement requis. Node.js est disponible dans Main et Renderer.
C true Nécessite un préchargement. Node.js est disponible en chargement principal et en préchargement, mais pas en rendu. Par défaut. Recommandé.
D true true Nécessite un préchargement. Node.js est disponible dans Main, Preload et Renderer.

Comment utiliser correctement le préchargement ?

Vous devez utiliser la communication inter-processus (IPC) d'Electron pour que le processus principal et le processus de rendu communiquent.

  1. Dans le processus main, utilisez :
  2. Dans le processus de préchargement, exposez un point de terminaison défini par l'utilisateur au processus de rendu.
  3. Dans le processus renderer, utilisez le point de terminaison défini par l'utilisateur public pour :
    • Envoyer un message à Main
    • Recevoir des messages de Main

Exemple de mise en œuvre

Principal

/**
 * 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 )
} )

Préchargé

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);

Que diriez-vous d'utiliser Promise ?

Gardez votre engagement envers le même processus/environnement autant que possible. Vos promesses sur main devraient rester sur main. Votre engagement envers le renderer doit également rester sur le moteur de rendu. Ne vous engagez pas à passer du programme principal au préchargeur puis au moteur de rendu.

Système de fichiers

La plupart de votre logique métier doit toujours être du côté principal ou du moteur de rendu, mais elle ne doit jamais être en préchargement. En effet, le préchargement existe presque exclusivement en tant que support. La précharge doit être très faible.

Dans le cas d'OP , fs fs doit être implémenté côté maître.

P粉323374878

Modifié 2022


J'ai publié un article plus volumineux sur l'histoire d'Electron

(Comment la sécurité a changé dans les versions d'Electron) et d'autres considérations de sécurité que les développeurs d'Electron peuvent prendre pour garantir une utilisation correcte du préchargement dans les nouveaux documents d'applications.

Modifié 2020

Comme un autre utilisateur l'a demandé, permettez-moi d'expliquer ma réponse ci-dessous.

preload.js 的正确方法是在您的应用可能需要 requireExposez un wrapper de liste blanche autour de n'importe quel module utilisant

dans Electron.

require 或通过 preload.js 中的 requireDu point de vue de la sécurité, exposer tout ce qui est récupéré par un appel est dangereux (voir mon commentaire

pour plus d'explications). Cela est particulièrement vrai si votre application charge du contenu distant (ce que font de nombreuses applications).

Pour faire cela correctement, vous devez définir la BrowserWindowrequire comme je le détaille ci-dessous. La définition de ces options force votre application Electron à communiquer via IPC (Inter-Process Communication) et isole les deux environnements l'un de l'autre. Configurer votre application de cette manière vous permet de valider tout ce qui dans le backend pourrait être un module require

sans qu'il soit falsifié par le client.

Ci-dessous, vous trouverez un court exemple de ce dont je parle et à quoi cela pourrait ressembler dans votre application. Si vous êtes nouveau, je pourrais vous recommander d'utiliser secure-electron-templatesecure-electron-template

(dont je suis l'auteur) pour intégrer toutes ces bonnes pratiques de sécurité dès le départ lors de la création d'applications électroniques.

Cette page

contient également de bonnes informations sur l'architecture requise lors de l'utilisation de preload.js pour créer des applications sécurisées.

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>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal