Electron: Erreur lors de l'importation d'électrons en réaction | importer { ipcRenderer } depuis 'electron'

Créé le 3 juil. 2017  ·  65Commentaires  ·  Source: electron/electron

J'ai créé une application de réaction simple avec create-react-app et je l'ai intégrée avec succès à electron. Tout fonctionnait très bien jusqu'à ce que j'essaye d'importer des électrons dans le fichier du créateur d'action. Si je supprime la ligne ci-dessous, l'application fonctionne correctement. Le problème est que je ne peux pas utiliser ipcRenderer pour communiquer du côté réactif au processus principal des électrons.

Cette ligne fait planter l'application :
import { ipcRenderer } from 'electron';

J'obtiens l'erreur suivante :

TypeError : fs.existsSync n'est pas une fonction
(fonction anonyme)
node_modules/electron/index.js:6

  3 | 
  4 | var pathFile = path.join(__dirname, 'path.txt')
  5 | 
> 6 | if (fs.existsSync(pathFile)) {
  7 |   module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
  8 | } else {
  9 |   throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')

J'ai découvert sur Google qu'il s'agit d'un problème courant lorsque l'on essaie d'importer des électrons.

Merci pour l'aide

Commentaire le plus utile

@MarshallOfSound mon erreur.

J'ai trouvé la solution dans le problème #7300 si cela peut aider quelqu'un.

const { ipcRenderer } = window.require('electron');

Veuillez noter que cela fonctionnera lorsque vous exécuterez l'application Electron, mais si vous souhaitez simplement tester votre code React dans le navigateur, il plantera toujours (window.require n'est pas défini dans le navigateur comme dans Electron).

Tous les 65 commentaires

CRA utilise webpack qui perturbe le chargement de module standard (y compris fs).

Je recommanderais d'examiner le mode Electron pour le pack Web et de l'éjecter de CRA

Les problèmes de GitHub concernent les demandes de fonctionnalités et les rapports de bogues, les questions sur l'utilisation d'Electron doivent être adressées à la communauté ou à Slack Channel .

@MarshallOfSound mon erreur.

J'ai trouvé la solution dans le problème #7300 si cela peut aider quelqu'un.

const { ipcRenderer } = window.require('electron');

Veuillez noter que cela fonctionnera lorsque vous exécuterez l'application Electron, mais si vous souhaitez simplement tester votre code React dans le navigateur, il plantera toujours (window.require n'est pas défini dans le navigateur comme dans Electron).

Si vous souhaitez accéder à app.quit(), vous pouvez utiliser ceci :

const { app } = window.require('electron').remote;

Peut-être que ça aide quelqu'un...

@CiriousJoker ce sont des solutions, merci !

Je reçois toujours window.require is not a function . J'utilise Electron avec React Starter Kit (https://github.com/kriasoft/react-starter-kit). Tout fonctionne bien, sauf ça.

J'ai configuré mon application Electron pour charger mon application à partir du Web, donc l'application ne s'exécute pas localement :
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6

Ce que j'essaie de faire, c'est d'appeler le ipcRenderer dans l'un de mes fichiers React. Je ne sais pas si c'est même possible lorsque mon application est chargée à partir du Web. Aucune suggestion?

@holgersindbaek

Dans le même bateau que vous... Avez-vous trouvé une solution ?

Non. Je suis presque sûr qu'il n'est pas possible de charger ipcRenderer à partir du navigateur.

Si vous exécutez votre application React dans le navigateur, cela ne fonctionnera pas. Exécutez-le dans Electron et tout devrait bien se passer.

@Amthieu Merci pour le conseil. Je ne sais toujours pas comment je peux exécuter mon projet React (basé sur React Starter Kit) dans Electron. Tout avis serait grandement apprécié:

https://discuss.atom.io/t/getting-electron-to-work-with-react-starter-kit/48594

D'accord, j'ai une solution.

1) Créez un preload.js file avec le code :

window.ipcRenderer = require('electron').ipcRenderer;

2) Préchargez ce fichier dans votre main.js via webPreferences :

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

3) Maintenant, vous aurez accès à partir de votre application React. Par exemple, cela fonctionnera :

componentDidMount() {
        if (isElectron()) {
            console.log(window.ipcRenderer);
            window.ipcRenderer.on('pong', (event, arg) => {
                this.setState({ipc: true})
            })
            window.ipcRenderer.send('ping')
        }
    }

Remarque - en utilisant ceci : https://github.com/cheton/is-electron pour la fonction isElectron()

@HemalR L' étape 3 devrait être la suivante (maintenant) :

componentDidMount() {
    if (window.isElectron) {
        console.log(window.ipcRenderer);
        window.ipcRenderer.on('pong', (event, arg) => {
            this.setState({ipc: true})
        })
        window.ipcRenderer.send('ping')
    }
}

Remarque : window.isElectron n'est pas une fonction.

@nparsons08

Excuses - j'aurais dû ajouter d'où je reçois isElectron, j'ai modifié mon exemple de code avec le lien vers : https://github.com/cheton/is-electron

@holgersindbaek
Y a-t-il une solution maintenant

pour moi, ne fonctionne que si nodeIntegration est true ;

webPreferences: {
      nodeIntegration: true, 
      preload: __dirname + '/preload.js'
}

fonctionne très bien la solution @HemalR !

maintenant COMMENT envoyer DE L'électron À React ?

essayé avec
du côté des électrons

 ipcMain.emit("pong", "Hello!"); 

mais rien n'a été reçu de l'auditeur React

window.ipcRenderer.on("pong", (event, arg) => {
        console.log("PONG");
});

est correct d'utiliser ipcMain.emit() ou dois-je utiliser autre chose ?

ok je viens de découvrir que je dois utiliser (sur le processus principal d'électrons)

mainWindow.webContents.send("pong", "Hello!");

merci à tous !

J'ai essayé tout ce qui précède en vain. Ce qui a fonctionné pour moi, c'est un hack géant. Modifiez le fichier ./node_modules/electron/index.js et codez en dur le chemin vers electron.exe

par exemple

function getElectronPath() {
  return 'D:\\Repos\\MyProject\\node_modules\\electron\\dist\\electron.exe';
}

module.exports = getElectronPath();

Wow, je n'ai pas pu faire fonctionner IPCRenderer sur mes composants React. J'ai essayé toutes les méthodes ci-dessus. L'un d'entre vous a-t-il d'ailleurs eu des indices que je peux utiliser pour que cela fonctionne ? Merci

Hmmm... Mon application électronique fonctionne toujours très bien en utilisant ma solution ci-dessus - mais je ne l'ai pas mise à jour depuis quelques mois maintenant (je n'en ai pas eu besoin).

Je me demande s'il y a un changement de rupture qui empêcherait cela de fonctionner? Peut-être que vous pouvez poster vos versions électroniques ?

@cyclonstep Y a-t-il une erreur spécifique que vous

J'utilise le colis pour le groupage.
Window.require l'a également résolu pour moi (montrant également, ce qui n'a pas été le cas):

importer Vue depuis 'vue/dist/vue.min'
importer l'application depuis './App'

// MAL? importer { ipcRenderer } depuis 'electron'
// MAL? const { ipcRenderer } = require('électron')
// BON:
const { ipcRenderer } = window.require('electron')

( plus bas dans le même fichier se trouve l'électron "pong-Demo" , ce qui prouve un peu que ça marche)

Peut-être à noter : même lorsque vous vous trompez, la taille du faisceau n'augmente pas (comparée à l'absence d'électrons requis. C'est jusqu'à présent ma première importation d'électrons côté rendu) de la taille entière de l'électron ou autre, mais seulement d'environ 20 ko , qui semble être un code shim/wrapper à lui seul, provenant de node_modules/electron-download/node_modules/debug/dist/debug.js :242:ff...

2: [function (require, module, exports) {
  // shim for using process in browser
  var process = module.exports = {}; // cached from whatever global is present so that test runners that
 stub it

Quoi qu'il en soit, les choses fonctionnent comme dit ci-dessus.

Noeud version 10.2.0
Chrome version 66.0.3359.181
Version électronique 3.0.2

window.require ne fonctionnait pas pour moi dans mon script principal avec l'erreur window is not defined , alors je suis passé à const electron = eval('require')("electron") . J'espère que cela aide quelqu'un. J'utilisais webpack, et le problème était que webpack évaluait mon instruction require au moment de la compilation.

@MarshallOfSound mon erreur.

J'ai trouvé la solution dans le problème #7300 si cela peut aider quelqu'un.

const { ipcRenderer } = window.require('electron');

Veuillez noter que cela fonctionnera lorsque vous exécuterez l'application Electron, mais si vous souhaitez simplement tester votre code React dans le navigateur, il plantera toujours (window.require n'est pas défini dans le navigateur comme dans Electron).

Et pour le tapuscrit :

import {IpcRenderer} from 'electron';

declare global {
  interface Window {
    require: (module: 'electron') => {
      ipcRenderer: IpcRenderer
    };
  }
}

const { ipcRenderer } = window.require('electron');

@moshfeu Votre solution fonctionne à

Pour taper et utiliser l'exemple de @HemalR ci-dessus mais SANS nodeIntegration: true : https://github.com/electron/electron/issues/9920#issuecomment -336757899 :

D'accord, j'ai une solution.

  1. Créez un preload.js file avec le code :
window.ipcRenderer = require('electron').ipcRenderer;
  1. Préchargez ce fichier dans votre main.js via webPreferences :
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });
  1. Maintenant, vous aurez accès à partir de votre application de réaction. Par exemple, cela fonctionnera :
componentDidMount() {
      if (isElectron()) {
          console.log(window.ipcRenderer);
          window.ipcRenderer.on('pong', (event, arg) => {
              this.setState({ipc: true})
          })
          window.ipcRenderer.send('ping')
      }
  }

Remarque - en utilisant ceci : https://github.com/cheton/is-electron pour la fonction isElectron()

combiné avec
https://github.com/electron/electron/issues/9920#issuecomment -447157348

J'ai utilisé ceci :

import { IpcRenderer } from 'electron';

declare global {
  interface Window {
    ipcRenderer: IpcRenderer
  }
}

export const { ipcRenderer } = window;

J'espère que cela aidera quelqu'un là-bas ! Fonctionne avec des stenciljs, et j'imagine réagir et angulaire

ajoutez simplement la cible : "electron-renderer" dans les configurations de webpack.
exporter par défaut {
...
cible : « restituteur d'électrons »
...
}

Bonjour, j'utilise CRA + Electron. Et je construis mon projet par cible 'electron-renderer'. Cela fonctionne bien lors du chargement de fichiers dans le dossier de construction, mais il génère une erreur lorsque je développe et charge l'url localhost:3000 . Je suppose que la raison en est que j'utilise node api et electron api dans les composants de réaction. Quelqu'un a la solution ? Merci.

TypeError: fs.existsSync is not a function

Oui, l'api fs n'est pas disponible dans le navigateur. Si je comprends bien la situation, vous exécutez votre application en utilisant react-scripts start (qui est le script par défaut de npm start dans CRA).

La bonne façon de procéder est d'exécuter electron . . Vous pouvez le voir dans la doc : https://electronjs.org/docs/tutorial/first-app.

LMK si cela fonctionne pour vous. Vous pouvez également voir comment cela fonctionne dans mon application - https://github.com/moshfeu/y2mp3 (Notez qu'il n'a pas été créé avec CRA)

Bonjour, j'utilise CRA + Electron. Et je construis mon projet par cible 'electron-renderer'. Cela fonctionne bien lors du chargement de fichiers dans le dossier de construction, mais il génère une erreur lorsque je développe et charge l'url localhost:3000 . Je suppose que la raison en est que j'utilise node api et electron api dans les composants de réaction. Quelqu'un a la solution ? Merci.

TypeError: fs.existsSync is not a function

ajoutez simplement la cible : "electron-renderer" dans les configurations de webpack.
exporter par défaut {
...
cible : « restituteur d'électrons »
...
}

Ça marche pour moi aussi.

Voici ma version d'outils :

  • Webpack : "3.8.1"
  • électron : "4.2.1"

Je pense que electron-renderer juste pour résoudre ce problème d'électrons. Je n'ai pas besoin d'utiliser une syntaxe filaire comme window.required , et il a même perdu la frappe !

Pour moi, j'ai travaillé sur la définition de la cible : "rendu d'électrons" pour le pack Web et l'intégration de nœud : vrai dans les options de BrowserWindow
J'utilise webpack+electron+react des dernières versions d'aujourd'hui

J'exécute create-react-app avec TypeScript et Electron. J'ai suivi ces très bonnes instructions pour une configuration. Mais j'ai aussi rencontré cette erreur. La solution qui fonctionne pour moi est la somme des choses dites dans ce fil:

  1. Faites en sorte que le script de réaction utilise "electron-renderer" comme target . J'utilise rescripts avec rescript-env pour cela.

package.json

  "rescripts": [
    "env"
  ],

.rescriptsrc.js

module.exports = [require.resolve("./webpack.config.js")] ;

webpack.config.js :

module.exports = config => {
  config.target = "electron-renderer";
  return config;
};
  1. Ajouter nodeIntegration
new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
});
  1. Ajouter src/typings.d.ts :
declare var window: Window;
interface Window {
  require: any;
}

Et toi enfin dans ton appli

App.tsx

const { remote } = window.require("electron");
console.log(remote.getCurrentWindow());

@LukasBombach
Cela devrait fonctionner aussi :

declare var window: Window;
interface Window {
  require: NodeRequire
}

Ensuite, vous aurez à taper sur les consts requis

Merci!

Voici les étapes que j'ai utilisées pour Vue au cas où cela serait utile à quelqu'un d'autre.

Assurez-vous que l'intégration des nœuds est activée pour le processus de rendu en l'ajoutant aux préférences Web lors de la création d'une fenêtre de navigateur :

new BrowserWindow({
    webPreferences: {
        nodeIntegration: true,
    },
})
````

Configure webpack to package your application for electron renderer by adding a target to your `vue.config.js` (or wherever you set your vue settings). 
```js
module.exports = {
    configureWebpack: {
        target: 'electron-renderer',
    },
}

Importez ce dont vous avez besoin dans votre application. J'importe le module shell dans mon composant comme ceci :

import { shell } from 'electron'

A tous ceux qui ont toujours le même problème. C'est la meilleure solution que j'ai trouvé jusqu'à présent

```js
nouvelle fenêtre de navigateur({
Préférences Web : {
nodeIntegration : vrai
}
});

J'espère que ce commentaire sera remarqué, car beaucoup de gens demandent à importer fs ou ipcRenderer dans vos applications. C'est un besoin commun pour les applications électroniques, mais j'ai trouvé que peu de gens avaient raison et utilisaient des modèles obsolètes. tl;dr - il y a une faille de sécurité si vous n'importez pas votre module de nœud (c'est-à-dire fs ) ou votre module électronique (c'est-à-dire ipcRenderer ) de la bonne manière. Si vous utilisez votre application uniquement pour vous-même, vous êtes _probablement_ en sécurité, mais si jamais vous souhaitez partager ou vendre votre application, vous devriez lire à l'avance.

Notre objectif

Avant d'entrer dans la solution, il est important de comprendre _pourquoi_ nous faisons cela en premier lieu. Les applications Electron nous permettent d'inclure des modules de nœuds dans nos applications, ce qui leur confère une puissance incroyable, mais des problèmes de sécurité. Nous voulons permettre à notre application d'utiliser les fonctionnalités natives du système d'exploitation (c'est-à-dire le nœud), mais nous ne voulons pas qu'elles soient utilisées de

Comme évoqué par nodeIntegration:true . Cependant, je choisirais toujours de garder nodeIntegration:false pour protéger les utilisateurs accidentels/malveillants utilisant votre application, et empêcher tout malware éventuel qui pourrait être installé sur votre machine d'interagir avec votre application électronique et d'utiliser le vecteur d'attaque nodeIntegration:true (incroyablement rare, mais pourrait arriver) !

La voie facile

La définition de nodeIntegration: true dans votre BrowserWindow donne à votre processus de rendu l'accès aux modules de nœuds. Faire _ce_, est vulnérable. Vous avez accès à require("fs") et require("electron") , mais cela signifie que si quelqu'un découvrait une vulnérabilité XSS, il pourrait exécuter n'importe quelle commande que vous avez exposée dans votre processus de rendu.

Pensez à supprimer tous les fichiers sur votre ordinateur, ou quelque chose d'autre qui est vraiment mauvais.

La solution (alternative) de facilité

En plus de définir nodeIntegration sur true, il est probable que votre application utilise webpack pour regrouper les fichiers d'application. Webpack perturbe certains symboles, donc des paramètres comme target: 'electron-renderer' ou les externes webpack vous permettent de transmettre ces variables ( ipcRenderer ) à votre application à la place.

Pourtant, cela ne change rien, sauf la façon dont vous configurez votre application.

L'autre solution de facilité

Vous pouvez utiliser le module distant qui vous donne accès à ipcRenderer . Il s'agit essentiellement de « la méthode facile » sous une forme différente. Cela n'est pas recommandé par les recommandations de sécurité d'Electron car ce type d'attaque souffre d'un prototype de vecteur de pollution.

C'est à dire. l'utilisation de la télécommande pourrait permettre à quelqu'un de modifier le prototype d'un objet js et de faire des ravages sur votre machine/application.

La _presque_ bonne voie

@marksyzm a une meilleure solution , bien que pas parfaite, où nous utilisons IPC pour envoyer le ipcRenderer au processus de rendu. Ce type de configuration est également vulnérable aux prototypes d' attaques de

Le droit chemin

La bonne façon d'importer vos fs / ipcRenderer dans votre processus de rendu est d'utiliser IPC (communication inter-processus). C'est la manière d'Electron de vous permettre de discuter entre le processus principal et le processus de rendu. Voici à quoi doit ressembler votre application :

  1. Un BrowserWindow a une propriété preload . Cette propriété est un fichier js qui se charge avec un accès à require (ce qui signifie que vous pouvez exiger ipcRenderer)
  2. Votre BrowserWindow disposera également de contextIsolation: true pour empêcher les prototypes d'attaques de pollution, mais cela signifie que vous devez utiliser le contextBridge pour transmettre l'ipcRenderer à votre processus de rendu.
  3. En utilisant le script de préchargement et le contextBridge, vous autorisez votre processus de rendu à accéder au ipcRenderer
  4. Dans votre script principal, vous créez des écouteurs pour ipcRenderer (dans le module ipcMain ). Dans ces écouteurs, vous pouvez utiliser le module fs

_En gros_ voici à quoi ressemblent toutes ces étapes :

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>

À tout le moins, je crois que vous avez besoin d'électrons v7 pour ces fonctionnalités.

Comment puis-je savoir cela?

Je me soucie des applications électroniques sécurisées et j'ai construit secure-electron-template afin de créer un modèle d'application électronique pour intégrer la sécurité au lieu de considérer la sécurité comme une réflexion après coup.

Sur la base du commentaire de preload.js , où mon API est plus proche de celle d'ipcRenderer.

main.js

let newWindow = null;
function createWindow() {
    newWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            preload: path.join(__dirname, "preload.js")
        }
    });

    newWindow.webContents.on('did-finish-load', () => {
        newWindow.webContents.send('APP_MY_INIT', { data: 'hi' });
    });
}

ipcMain.on('APP_SOMETHING', (event, ...args) => {
    // ...
});

ipcMain.handle('APP_SOMETHING_ELSE', (event, ...args) => {
    // ...
    return myVar;
});

preload.js

const { contextBridge, ipcRenderer } = require('electron');

function callIpcRenderer(method, channel, ...args) {
    if (typeof channel !== 'string' || !channel.startsWith('APP_')) {
        throw 'Error: IPC channel name not allowed';
    }
    if (['invoke', 'send'].includes(method)) {
        return ipcRenderer[method](channel, ...args);
    }
    if ('on' === method) {
        const listener = args[0];
        if (!listener) throw 'Listener must be provided';

        // Wrap the given listener in a new function to avoid exposing
        // the `event` arg to our renderer.
        const wrappedListener = (_event, ...a) => listener(...a);
        ipcRenderer.on(channel, wrappedListener);

        // The returned function must not return anything (and NOT
        // return the value from `removeListener()`) to avoid exposing ipcRenderer.
        return () => { ipcRenderer.removeListener(channel, wrappedListener); };
    }
}

contextBridge.exposeInMainWorld(
    'myIpcRenderer', {
        invoke: (...args) => callIpcRenderer('invoke', ...args),
        send: (...args) => callIpcRenderer('send', ...args),
        on: (...args) => callIpcRenderer('on', ...args),
    },
);

client.js

const { myIpcRenderer } = window;

const removeMyListener = myIpcRenderer.on('APP_MY_INIT', data => {
    console.log(data);
    myIpcRenderer.send('APP_SOMETHING', 'foo');
})

async function test() {
    const result = await myIpcRenderer.invoke('APP_SOMETHING_ELSE', 'foo', 'bar');
    console.log(result);
}
test();

if (/* should remove listener === true */) {
    removeMyListener();
}

Et pour ceux qui utilisent TypeScript, types.d.ts

declare global {
    interface Window {
        myIpcRenderer: MyIpcRenderer,
    }
}

export interface MyIpcRenderer {
    invoke(channel: string, ...args: any[]): Promise<any>;
    send(channel: string, ...args: any[]): void;

    /** <strong i="22">@return</strong> A function that removes this listener. */
    on(channel: string, listener: (...args: any[]) => void): () => void;
}

Est-ce que quelqu'un sait pourquoi j'obtiens une erreur de module non trouvé lorsque j'essaie de le mettre dans preload.js ?

const errorLogging = require('../renderer/utils/errorLogging');

Merci

@silentlight ce n'est pas lié à l'électron, avez-vous peut-être votre propre require() n'est pas correct et c'est pourquoi il renvoie une erreur, sans voir plus de code).

Bien qu'il existe déjà la solution window.require , je vais partager la mienne, juste pour le fun :

function magic(module){
    require(module)
}

const { ipcRenderer } = magic('electron')

Vos bundlers ne s'identifieront pas lorsque vous importez un module car vous n'utilisez pas de modules ES ou de syntaxe CommonJS, il n'essaiera donc pas de le regrouper et ne générera donc pas d'erreur

Je me demande pourquoi personne n'a encore essayé ça 😆

@marc2332 true, mais vous vous exposez alors directement à des attaques potentielles de ce wrapper magique. Cela peut ne pas s'appliquer à votre scénario, mais c'est certainement une préoccupation que vous devriez considérer.

@reZach Je ne vois aucune raison pour laquelle le window.require est plus sécurisé pour être honnête. Au fait, il y a des façons plus sales de le faire.

const { ipcRenderer } = eval("require('electron')")

@ marc2332 ni l'

Merci reZach ! Je pense que les gens comprendront votre approche plus tôt

ou plus tard

Mark Elphinstone
www.oxfordsourceltd.com

Salut Zach, la solution contextBridge charge les prototypes ?

Mon code d'origine avec contextIsolation: false simplement attaché une fonction sur window . Cette fonction renvoie un Promise<MediaStream> mais la documentation de contextBridge indique que les prototypes sont supprimés et que lorsque j'ai utilisé le contextBridge je reçois un objet vide.

Existe-t-il un moyen de prendre en charge les prototypes mais également de garantir contextIsolation: true ?

Salut Zach, la solution contextBridge charge les prototypes ?

Mon code d'origine avec contextIsolation: false simplement attaché une fonction sur window . Cette fonction renvoie un Promise<MediaStream> mais la documentation de contextBridge indique que les prototypes sont supprimés et que lorsque j'ai utilisé le contextBridge je reçois un objet vide.

Existe-t-il un moyen de prendre en charge les prototypes mais également de garantir contextIsolation: true ?

Non, ce n'est pas le cas. J'essayais de trouver un commentaire dont je me souviens par l'un des membres de l'équipe selon lequel il s'agit d'un comportement intentionnel, mais je n'ai pas pu. Le mieux que je puisse dire est dans Electron v8, ( pr 20214 ) a apporté des modifications à la sérialisation des objets dans IPC (effectivement lorsque l'isolation du contexte est

REMARQUE : Les objets qui ne sont pas sérialisables avec l'algorithme de clonage structuré de V8, tels que les fonctions, les objets DOM, les objets Node/Electron spéciaux comme process.env ou WebContents, ou tout objet contenant de tels éléments seront sérialisés avec l'ancienne base : : Value- algorithme basé. Cependant, ce comportement est obsolète et lèvera une exception à partir de Electron 9.

Ce n'est probablement pas la nouvelle que vous vouliez entendre, mais j'espère avoir aidé.

Salut Zach, la solution contextBridge charge les prototypes ?
Mon code d'origine avec contextIsolation: false simplement attaché une fonction sur window . Cette fonction renvoie un Promise<MediaStream> mais la documentation de contextBridge indique que les prototypes sont supprimés et que lorsque j'ai utilisé le contextBridge je reçois un objet vide.
Existe-t-il un moyen de prendre en charge les prototypes mais également de garantir contextIsolation: true ?

Non, ce n'est pas le cas. J'essayais de trouver un commentaire dont je me souviens par l'un des membres de l'équipe selon lequel il s'agit d'un comportement intentionnel, mais je n'ai pas pu. Le mieux que je puisse dire est dans Electron v8, ( pr 20214 ) a apporté des modifications à la sérialisation des objets dans IPC (effectivement lorsque l'isolation du contexte est

REMARQUE : Les objets qui ne sont pas sérialisables avec l'algorithme de clonage structuré de V8, tels que les fonctions, les objets DOM, les objets Node/Electron spéciaux comme process.env ou WebContents, ou tout objet contenant de tels éléments seront sérialisés avec l'ancienne base : : Value- algorithme basé. Cependant, ce comportement est obsolète et lèvera une exception à partir de Electron 9.

Ce n'est probablement pas la nouvelle que vous vouliez entendre, mais j'espère avoir aidé.

C'est dommage, mais merci pour ta réponse rapide.

J'ai suivi les étapes décrites ci-dessus (données par @reZach et @aplum), et je transmets des informations de mon client.js au preload.js à main.js .

Lorsque j'exécute tout localement, la communication fonctionne, mais lorsque j'exécute electron-builder , la communication est interrompue. Par exemple, l'utilisation de setBadge() fonctionne à merveille lorsqu'elle est exécutée localement. Je peux prendre le compte du fichier client.js et le voir être passé dans le contextBridge puis dans main.js et définir avec succès le app.dock.setBadge(count) . Y a-t-il quelque chose qui me manque avec la communication dans Electron après sa construction avec electron-builder ?.

"electron": "^8.2.3",
"electron-builder": "^22.4.0",

Main.js

const { ipcMain } = electron;

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        height: height,
        width: width,
        minHeight: 575,
        minWidth: 900,
        center: true,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            spellcheck: true,
            preload: path.join(__dirname, "preload.js")
        },
    });
}

ipcMain.on('SEND_BADGE_COUNT', (e, count) => {
    const badgeCount = count > 0 ? count : '';
    app.dock.setBadge(`${badgeCount}`)
})

Précharger.js

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld(
    'setBadgeCountForElectron', {
        send: (channel, data) => {
            console.log(`Preload ${channel}, ${data}`)
            ipcRenderer.send(channel, data)
        }
    }
)

Client.js

const { setBadgeCountForElectron } = window;

function sendBadgeCount(count) {
  !!setBadgeCountForElectron && setBadgeCountForElectron.send('SEND_BADGE_COUNT', count)
}

sendBadgeCount(count)

package.json

  "name": "desktop_app",
  "version": "0.1.8-beta",
  "private": true,
  "description": "",
  "homepage": "./",
  "main": "public/electron.js",
  "build": {
    "copyright": "Copyright © 2020 My Company",
    "appId": "com.my-app.app",
    "productName": "My Company",
    "buildVersion": "0.0.1-beta",
    "mac": {
      "category": "public.app-category.utilities",
      "icon": "./public/images/mac-icon.png"
    },
    "win": {
      "icon": "./public/images/windows-icon.png"
    },
    "files": [
      "build/**/*",
      "node_modules/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    }
  },

@SterlingChin , je ne suis pas un responsable de l'équipe d'électrons, mais cela pourrait être une meilleure question adaptée au référentiel de constructeurs d'électrons.

Je suis plus enclin à penser qu'il s'agit d'un problème de code plutôt que de générateur d'électrons lorsque j'ai pris un moment pour y réfléchir.

Je suis plus enclin à penser qu'il s'agit d'un problème de code plutôt que de générateur d'électrons lorsque j'ai pris un moment pour y réfléchir.

Je suis sûr qu'il me manque un élément clé qui permettrait à tout cela de fonctionner comme prévu. Merci pour votre réponse. J'ai ouvert un problème dans le electron-builder , mais il n'a reçu aucune traction.

Je suis plus enclin à penser qu'il s'agit d'un problème de code plutôt que de générateur d'électrons lorsque j'ai pris un moment pour y réfléchir.

Je suis sûr qu'il me manque un élément clé qui permettrait à tout cela de fonctionner comme prévu. Merci pour votre réponse. J'ai ouvert un problème dans le electron-builder , mais il n'a reçu aucune traction.

Avez-vous créé un dépôt MVP avec votre problème ? Cela aide parfois et dans le processus, vous réalisez ce qui se passe.

@Amthieu et @CiriousJoker je vous aime tous les deux merci.

  1. fil ajouter -D customiser-cra réagir-app-recâblé
  2. modifiez package.json pour utiliser l'application de démarrage react-app-rewired : "start": "BROWSER=none react-app-rewired start",
  3. créer le fichier 'config-overrides' dans le répertoire racine de l'application de réaction :
    `const { override } = require('customize-cra');

function addRendererTarget(config) {
config.target = 'electron-renderer';

retourner la configuration ;
}

module.exports = override(addRendererTarget);`

  1. vous pouvez importer { ipcRenderer} depuis 'electron' maintenant :)

J'utilise vuejs et ajoute simplement le code dans vue.config.js

module.exports = {
"transpileDependencies": [
"vuetifier"
],
pluginOptions : {
electronBuilder : {
nodeIntegration : vrai
}
}
}

@genilsonmm Putain de merde qui a fonctionné ! Je deviens fou depuis 3 heures. Merci!!
La partie spécifique de cela qui a fonctionné pour moi était le
```javascript
pluginOptions : {
electronBuilder : {
nodeIntegration : vrai
}
},

"transpileDependencies": [
"vuetifier"
],

cela ne fonctionne pas sur moi... mais window.require('electron') fonctionne bien

Pour toutes les personnes qui ont le problème "window.require n'est pas une fonction"

Vous devez créer un script de préchargement sur electron.

  1. Créez un fichier nommé preload.js dans le répertoire où vous avez le script principal d'électron, placez ce code dessus :

    window.require = exiger;

  2. Accédez à votre script principal d'électron et tapez ceci dans le code où vous créez la fenêtre :

    win = new BrowserWindow({
    largeur : 1280,
    hauteur: 720,
    Préférences Web : {
    nodeIntegration : faux,
    précharger : __dirname + '/preload.js'
    },

    })
    Avec cela, vous allez précharger le script avant tout, cela a résolu le problème pour moi. j'espère aussi pour toi :)

@reZach, votre solution a fonctionné pour moi, mais j'ai remarqué une erreur sur laquelle d'autres personnes pourraient trébucher (parce que je l'ai fait, oups !):

        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => fn(...args));
            }
        }

vous définissez votre fonction de rappel comme "func" , mais appelez ensuite "fn", si vous modifiez cela, cela fonctionne exactement comme vous le décrivez.
Un grand merci pour le post détaillé :+1:

@genilsonmm Si je mets vue.config.js :

pluginOptions : {
electronBuilder : {
nodeIntegration : vrai,

J'obtiens l'erreur "require is notdefined":

image

Si je commente la ligne "nodeIntegration: true", le message d'erreur disparaît et l'application fonctionne.

Qu'est-ce que ça veut dire?

@marcoippolito Je pense que cela signifie que le code fourni avec le pack Web essaie d'utiliser require qui est la méthode de résolution de module de node js. Comme cela n'est pas disponible si vous désactivez l'intégration de nœud, le code ne peut pas s'exécuter. Ce que vous devez faire est de modifier votre configuration webpack pour cibler les navigateurs ( var est la cible si je me souviens bien) et assurez-vous qu'aucun de votre code n'utilise node apis et l'erreur devrait disparaître.

Pour ceux qui utilisent TypeScript, il y a un bon exemple dans les exemples officiels de Next.js :
https://github.com/vercel/next.js/search?q=ipcRenderer&unscoped_q=ipcRenderer

preload.ts

/* eslint-disable @typescript-eslint/no-namespace */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { ipcRenderer, IpcRenderer } from 'electron'

declare global {
  namespace NodeJS {
    interface Global {
      ipcRenderer: IpcRenderer
    }
  }
}

// Since we disabled nodeIntegration we can reintroduce
// needed node functionality here
process.once('loaded', () => {
  global.ipcRenderer = ipcRenderer
})

index.tsx

import { useState, useEffect } from 'react'

const Home = () => {
  const [input, setInput] = useState('')
  const [message, setMessage] = useState(null)

  useEffect(() => {
    const handleMessage = (event, message) => setMessage(message)
    global.ipcRenderer.on('message', handleMessage)

    return () => {
      global.ipcRenderer.removeListener('message', handleMessage)
    }
  }, [])

  const handleSubmit = (event) => {
    event.preventDefault()
    global.ipcRenderer.send('message', input)
    setMessage(null)
  }

  return (
    <div>
      <h1>Hello Electron!</h1>
    </div>
  )
}

export default Home

@genilsonmm Putain de merde qui a fonctionné ! Je deviens fou depuis 3 heures. Merci!!
La partie spécifique de cela qui a fonctionné pour moi était le

   pluginOptions: {
        electronBuilder: {
            nodeIntegration: true
        }
    },

??

J'ai aussi ce problème avec NodeIntegration activé. Les deux window.require et require ne fonctionnent pas.

+ Cela ne se produit qu'avec des électrons réactifs, pas simples

Cette page vous a été utile?
0 / 5 - 0 notes