Electron: Fehler beim Importieren des Elektrons in die Reaktion | import { ipcRenderer } von 'elektron'

Erstellt am 3. Juli 2017  ·  65Kommentare  ·  Quelle: electron/electron

Ich habe mit create-react-app eine einfache React-App erstellt und diese erfolgreich in Electron integriert. Alles hat super funktioniert, bis ich versucht habe, Elektron in die Action-Creator-Datei zu importieren. Wenn ich die Zeile darunter entferne, funktioniert die App einwandfrei. Das Problem ist, dass ich den ipcRenderer nicht verwenden kann, um von der Reaktionsseite zum Elektronenhauptprozess zu kommunizieren.

Diese Zeile führt zum Absturz der App:
import { ipcRenderer } from 'electron';

Ich bekomme folgenden Fehler:

TypeError: fs.existsSync ist keine Funktion
(anonyme Funktion)
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')

Ich habe bei Google herausgefunden, dass dies ein häufiges Problem beim Versuch ist, Elektron zu importieren.

Danke für die Hilfe

Hilfreichster Kommentar

@MarshallOfSound mein Fehler.

Ich habe die Lösung in Problem #7300 gefunden, wenn es jemandem helfen kann.

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

Bitte beachten Sie, dass dies funktioniert, wenn Sie die Electron-App ausführen, aber wenn Sie Ihren React-Code nur im Browser testen möchten, stürzt er immer noch ab (window.require ist im Browser nicht wie in Electron definiert).

Alle 65 Kommentare

CRA verwendet Webpack, das das Laden von Standardmodulen (einschließlich fs) durcheinander bringt.

Ich würde empfehlen, in den Electron-Modus für Webpack zu schauen und aus CRA auszuwerfen

GitHub-Probleme beziehen sich auf Funktionsanfragen und Fehlerberichte, Fragen zur Verwendung von Electron sollten an die Community oder an den Slack Channel gerichtet werden .

@MarshallOfSound mein Fehler.

Ich habe die Lösung in Problem #7300 gefunden, wenn es jemandem helfen kann.

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

Bitte beachten Sie, dass dies funktioniert, wenn Sie die Electron-App ausführen, aber wenn Sie Ihren React-Code nur im Browser testen möchten, stürzt er immer noch ab (window.require ist im Browser nicht wie in Electron definiert).

Wenn Sie auf app.quit() zugreifen möchten, können Sie dies verwenden:

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

Vielleicht hilft es jemandem...

@CiriousJoker das sind Lösungen, danke!

Ich bekomme immer noch window.require is not a function . Ich verwende Electron mit React Starter Kit (https://github.com/kriasoft/react-starter-kit). Alles funktioniert gut, außer diesem.

Ich habe meine Electron-App so eingestellt, dass sie meine App aus dem Web lädt, sodass die App nicht lokal ausgeführt wird:
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6

Was ich versuche, ist ipcRenderer in einer meiner React-Dateien aufzurufen. Ich bin mir jedoch nicht sicher, ob dies überhaupt möglich ist, wenn meine App aus dem Web geladen wird. Irgendwelche Vorschläge?

@holgersindbaek

Im selben Boot wie Sie... Haben Sie eine Lösung gefunden?

Nein. Ich bin mir ziemlich sicher, dass es nicht möglich ist, den ipcRenderer aus dem Browser zu laden.

Wenn Sie Ihre React-App im Browser ausführen, funktioniert sie nicht. Führen Sie es in Electron aus und es sollte Ihnen gut gehen.

@Amthieu Danke für den Rat. Ich bin immer noch im Zweifel, wie ich mein React-Projekt (basierend auf dem React Starter Kit) in Electron zum Laufen bringen kann. Jeder Rat wäre sehr dankbar:

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

Stimmt, ich habe eine Lösung.

1) Erstellen Sie ein preload.js file mit dem Code:

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

2) Laden Sie diese Datei über webPreferences in Ihre main.js:

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

3) Jetzt haben Sie Zugriff von Ihrer React-App. Das wird zB funktionieren:

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

Hinweis - Verwenden Sie dies: https://github.com/cheton/is-electron für die Funktion isElectron()

@HemalR Schritt 3 sollte (jetzt) ​​wie folgt sein:

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

Hinweis : window.isElectron ist keine Funktion.

@nparsons08

Entschuldigung - hätte hinzufügen sollen, woher ich isElectron bekomme, habe mein Codebeispiel mit dem Link zu: https://github.com/cheton/is-electron bearbeitet

@holgersindbaek
Gibt es jetzt eine Lösung

für mich nur funktionieren, wenn nodeIntegration true ;

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

funktioniert super mit der

jetzt WIE sendet man VOM Elektron ZU Reagieren?

versucht mit
auf der Elektronenseite

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

aber vom React-Hörer wurde nichts empfangen

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

ist es richtig, ipcMain.emit() zu verwenden, oder sollte ich etwas anderes verwenden?

ok gerade gefunden, dass ich verwenden muss (auf dem Elektronenhauptprozess)

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

Danke an alle!

Ich habe alle oben genannten erfolglos versucht. Was für mich funktionierte, war ein riesiger Hack. Ändern Sie die Datei ./node_modules/electron/index.js und codieren Sie den Pfad zu electron.exe hart

z.B

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

module.exports = getElectronPath();

Wow, ich konnte den IPCRenderer nicht zum Laufen bringen auf meinen React Components. Ich habe alle oben genannten Methoden ausprobiert. Hat jemand von euch zufällig irgendwelche Hinweise, die ich verwenden kann, damit es funktioniert? Danke

Hmmm ... Meine Elektron-App funktioniert mit meiner obigen Lösung immer noch einwandfrei - aber ich habe sie seit ein paar Monaten nicht mehr aktualisiert (hatte es nicht).

Ich frage mich, ob es eine bahnbrechende Änderung gibt, die dies daran hindern würde, zu funktionieren? Vielleicht könnt ihr eure Elektronenversionen posten?

@cyclonstep Gibt es einen bestimmten Fehler, den Sie erhalten haben? Ohne ein Code-Snippet oder ein paar Logs schwer zu helfen...

Ich verwende Paket zum Bündeln.
Window.require hat es auch für mich gelöst (zeigt auch, was nicht):

Vue aus 'vue/dist/vue.min' importieren
App aus './App' importieren

// SCHLECHT? import { ipcRenderer } von 'elektron'
// SCHLECHT? const { ipcRenderer } = require('Elektron')
// GUT:
const { ipcRenderer } = window.require('elektron')

(weiter unten in der gleichen Datei ist die Electron „Pong-Demo“ , die irgendwie beweist, dass es funktioniert)

Vielleicht bemerkenswert: Auch wenn es falsch gemacht wird, wächst die Bündelgröße nicht um die gesamte Elektronengröße oder so, sondern nur um etwa 20kb , der ein eigener Shim-/Wrapper-Code zu sein scheint, der von 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

Auf jeden Fall funktioniert alles wie oben beschrieben.

Knotenversion 10.2.0
Chrome-Version 66.0.3359.181
Elektronenversion 3.0.2

window.require funktionierte bei mir in meinem Hauptskript nicht mit dem Fehler window is not defined , also wechselte ich zu const electron = eval('require')("electron") . Hoffe das hilft jemandem. Bei der Verwendung von Webpack bestand das Problem darin, dass Webpack meine Anforderungserklärung zum Zeitpunkt der Kompilierung auswertete.

@MarshallOfSound mein Fehler.

Ich habe die Lösung in Problem #7300 gefunden, wenn es jemandem helfen kann.

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

Bitte beachten Sie, dass dies funktioniert, wenn Sie die Electron-App ausführen, aber wenn Sie Ihren React-Code nur im Browser testen möchten, stürzt er immer noch ab (window.require ist im Browser nicht wie in Electron definiert).

Und für Typoskript:

import {IpcRenderer} from 'electron';

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

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

@moshfeu Deine Lösung funktioniert fantastisch. Ich benötige weder Webpack noch Browserfy, um IpcRenderer in meinem React-Projekt zu verwenden. Vielen Dank nochmal :D

Für Typoskript und Verwendung des obigen Beispiels von nodeIntegration: true : https://github.com/electron/electron/issues/9920#issuecomment -336757899:

Stimmt, ich habe eine Lösung.

  1. Erstellen Sie ein preload.js file mit dem Code:
window.ipcRenderer = require('electron').ipcRenderer;
  1. Laden Sie diese Datei vorab in Ihre main.js über webPreferences :
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });
  1. Jetzt haben Sie Zugriff von Ihrer React-App. Das wird zB funktionieren:
componentDidMount() {
      if (isElectron()) {
          console.log(window.ipcRenderer);
          window.ipcRenderer.on('pong', (event, arg) => {
              this.setState({ipc: true})
          })
          window.ipcRenderer.send('ping')
      }
  }

Hinweis - Verwenden Sie dies: https://github.com/cheton/is-electron für die Funktion isElectron()

kombiniert mit
https://github.com/electron/electron/issues/9920#issuecomment -447157348

Ich habe das verwendet:

import { IpcRenderer } from 'electron';

declare global {
  interface Window {
    ipcRenderer: IpcRenderer
  }
}

export const { ipcRenderer } = window;

Hoffe das hilft jemandem da draußen! Funktioniert mit Schablonen, und ich stelle mir reagieren und eckig vor

fügen Sie einfach target: "electron-renderer" in den Webpack-Konfigurationen hinzu.
Exportstandard {
...
Ziel: "Elektronen-Renderer"
...
}

Hallo, ich benutze CRA + Electron. Und ich baue mein Projekt nach dem Ziel-'Elektronen-Renderer'. Es funktioniert gut beim Laden von Dateien im Build-Ordner, aber es wird ein Fehler ausgegeben, wenn ich die URL localhost:3000 entwickle und lade. Ich denke, der Grund ist, dass ich Knoten-API und Elektronen-API in Reaktionskomponenten verwende. Hat jemand die Lösung? Danke.

TypeError: fs.existsSync is not a function

Ja, die fs API ist im Browser nicht verfügbar. Wenn ich die Situation richtig verstehe, führen Sie Ihre App mit react-scripts start (das ist das Standardskript von npm start in CRA).

Der richtige Weg, dies zu tun, ist electron . auszuführen. Sie können es in den Dokumenten sehen: https://electronjs.org/docs/tutorial/first-app.

LMK, wenn es für Sie funktioniert. Sie können auch sehen, wie es in meiner App funktioniert - https://github.com/moshfeu/y2mp3 (Beachten Sie, dass es nicht mit CRA erstellt wurde)

Hallo, ich benutze CRA + Electron. Und ich baue mein Projekt nach dem Ziel-'Elektronen-Renderer'. Es funktioniert gut beim Laden von Dateien im Build-Ordner, aber es wird ein Fehler ausgegeben, wenn ich die URL localhost:3000 entwickle und lade. Ich denke, der Grund ist, dass ich Knoten-API und Elektronen-API in Reaktionskomponenten verwende. Hat jemand die Lösung? Danke.

TypeError: fs.existsSync is not a function

fügen Sie einfach target: "electron-renderer" in den Webpack-Konfigurationen hinzu.
Exportstandard {
...
Ziel: "Elektronen-Renderer"
...
}

Bei mir funktioniert es auch.

Hier ist meine Tools-Version:

  • Webpack: "3.8.1"
  • Elektron: "4.2.1"

Ich denke, electron-renderer nur um dieses Elektronenproblem zu lösen. Ich brauche keine verdrahtete Syntax wie window.required , und es verlor sogar die Eingabe!

Bei mir hat das Einstellungsziel funktioniert: "Elektronen-Rendering" für Webpack und NodeIntegration: true in BrowserWindow-Optionen
Ich verwende webpack+electron+react der letzten Versionen von heute

Ich führe create-react-app mit TypeScript und Electron aus. Ich habe diese sehr gute Anleitung für ein Setup befolgt. Aber dann bin ich auch auf diesen Fehler gestoßen. Die Lösung, die für mich funktioniert, ist die Summe der Dinge, die in diesem Thread gesagt wurden:

  1. Lassen Sie das React-Skript "electron-renderer" als target . Dafür verwende ich rescripts mit rescript-env .

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. KnotenIntegration hinzufügen
new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
});
  1. src/typings.d.ts :
declare var window: Window;
interface Window {
  require: any;
}

Und endlich in deiner App

App.tsx

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

@LukasBombach
Das sollte auch funktionieren:

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

Dann müssen Sie die erforderlichen Konstanten eingeben

Danke!

Hier sind die Schritte, die ich für Vue verwendet habe, falls es für jemand anderen hilfreich ist.

Stellen Sie sicher, dass die Knotenintegration für den Renderer-Prozess aktiviert ist, indem Sie sie beim Erstellen eines Browserfensters zu den Webeinstellungen hinzufügen:

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',
    },
}

Importieren Sie, was Sie in Ihrer Anwendung benötigen. Ich importiere das Shell-Modul wie folgt in meine Komponente:

import { shell } from 'electron'

An alle die immer noch das gleiche Problem haben. Das ist die beste Lösung die ich bisher gefunden habe

```js
neues BrowserWindow({
Webeinstellungen: {
KnotenIntegration: wahr
}
});

Ich hoffe, dass dieser Kommentar auffällt, denn viele Leute fragen nach dem Importieren von fs oder ipcRenderer in Ihre Apps. Elektron-Apps werden häufig benötigt, aber ich habe festgestellt, dass nicht viele Leute es richtig machen und veraltete Muster verwenden. tl;dr - Es gibt eine Sicherheitslücke, wenn Sie Ihr Node-Modul (zB fs ) oder Elektronenmodul (zB ipcRenderer ) nicht korrekt importieren. Wenn Sie Ihre App nur für sich selbst verwenden, sind Sie _wahrscheinlich_ sicher, aber wenn Sie Ihre App jemals teilen oder verkaufen möchten, sollten Sie weiterlesen.

Unser Ziel

Bevor ich auf die Lösung eingehe, ist es wichtig zu verstehen, _warum_ wir dies überhaupt tun. Electron-Apps ermöglichen es uns, Knotenmodule in unsere Apps einzubinden, was ihnen erstaunliche Leistung verleiht, aber Sicherheitsbedenken bietet. Wir möchten, dass unsere App native OS-Funktionen (z. B. Knoten) verwendet, aber wir möchten nicht, dass sie missbraucht werden .

Wie von @raddevus in einem Kommentar erwähnt, ist dies beim Laden von Remote-Inhalten erforderlich. Wenn Ihre Elektron-App vollständig offline/lokal ist, ist es wahrscheinlich in Ordnung, einfach nodeIntegration:true . Ich würde mich jedoch dennoch dafür entscheiden, nodeIntegration:false zu behalten, um als Schutz für versehentliche/böswillige Benutzer, die Ihre App verwenden, zu dienen und zu verhindern, dass mögliche Malware, die jemals auf Ihrem Computer installiert werden könnte, mit Ihrer Electron-App interagiert und verwendet der Angriffsvektor nodeIntegration:true (unglaublich selten, aber möglich)!

Der einfache Weg

Das Setzen von nodeIntegration: true in Ihrem BrowserWindow gibt Ihrem Renderer-Prozess Zugriff auf Knotenmodule. _this_ zu tun, ist verwundbar. Sie haben Zugriff auf require("fs") und require("electron") , aber das bedeutet, wenn jemand eine XSS-Sicherheitslücke findet, könnte er jeden Befehl ausführen, den Sie in Ihrem Renderer-Prozess offengelegt haben.

Denken Sie daran, alle Dateien auf Ihrem Computer zu löschen, oder etwas anderes, das wirklich schlecht ist.

Der (alternative) einfache Weg

Neben dem Festlegen von nodeIntegration auf true verwendet Ihre App wahrscheinlich Webpack, um Anwendungsdateien zu bündeln. Webpack bringt bestimmte Symbole durcheinander, daher können Sie mit Einstellungen wie target: 'electron-renderer' oder Webpack-Externals diese Variablen ( ipcRenderer ) stattdessen an Ihre App übergeben.

Dies ändert jedoch nichts, außer wie Sie Ihre App einrichten.

Der (andere-alternative) einfache Weg

Sie können das Remote-Modul verwenden , mit dem Sie auf ipcRenderer zugreifen können. Es ist im Grunde „der einfache Weg“ in einer anderen Form. Es wird nicht empfohlen , durch Elektronensicherheits Empfehlungen diesen Angriff leidet , da diese Art von Prototyp Verschmutzung Vektor zu tun.

Dh. Die Verwendung von Remote könnte es jemandem ermöglichen, den Prototyp eines js-Objekts zu ändern und Ihr Gerät/Ihre App zu zerstören.

Der _fast_ richtige Weg

@marksyzm hat eine bessere , wenn auch nicht perfekte ipcRenderer an den Renderer-Prozess zu senden. Diese Art von Setup ist auch anfällig für Angriffe durch die Verschmutzung durch

Der richtige Weg

Der richtige Weg, Ihre fs / ipcRenderer in Ihren Renderer-Prozess zu importieren, ist mit IPC (Inter-Process-Communication). Auf diese Weise ermöglicht Electron Ihnen die Kommunikation zwischen Haupt- und Rendererprozess. Aufgeschlüsselt muss Ihre App so aussehen:

  1. Ein BrowserWindow hat eine preload Eigenschaft. Diese Eigenschaft ist eine js-Datei, die mit Zugriff auf require geladen wird (was bedeutet, dass Sie ipcRenderer benötigen)
  2. Ihr BrowserWindow verfügt auch über contextIsolation: true , um Angriffe durch Verschmutzung durch Prototypen zu verhindern, aber das bedeutet, dass Sie die contextBridge verwenden müssen , um den ipcRenderer an Ihren Renderer-Prozess zu übergeben
  3. Mit dem Preload-Skript und der contextBridge erlauben Sie Ihrem Renderer-Prozess den Zugriff auf ipcRenderer
  4. In Ihrem Hauptskript erstellen Sie Listener für den ipcRenderer (im Modul ipcMain ). Innerhalb dieser Listener können Sie das Modul fs

_In etwa_ sehen all diese Schritte so aus:

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>

Zumindest _glaube_ ich, dass Sie für diese Funktionen Electron v7 benötigen.

Woher weiß ich das?

Ich interessiere mich für sichere Elektron-Apps und habe secure-electron-template , um eine Elektronenanwendungsvorlage zu erstellen, um die Sicherheit zu integrieren, anstatt an Sicherheit im Nachhinein zu denken.

Aufbauend auf dem obigen Kommentar von preload.js , wo meine API näher an der von ipcRenderer liegt.

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

Und für diejenigen, die TypeScript verwenden, 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;
}

Weiß jemand, warum ich die Fehlermeldung "Modul nicht gefunden" bekomme, wenn ich versuche, dies in preload.js einzufügen?

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

Danke

@silentlight dies hat nichts mit Elektron zu tun, haben Sie vielleicht ein eigenes Github-Repository, in dem die Konversation fortgesetzt werden kann? (Es sieht so aus, als ob Ihr Pfad in require() nicht korrekt ist und deshalb wird ein Fehler ausgegeben, ohne dass mehr Code angezeigt wird).

Obwohl es bereits die window.require Lösung gibt, werde ich meine nur zum Spaß teilen:

function magic(module){
    require(module)
}

const { ipcRenderer } = magic('electron')

Ihre Bundler werden sich beim Importieren eines Moduls nicht identifizieren, da Sie keine ES-Module oder die CommonJS-Syntax verwenden

Ich frage mich warum das noch keiner probiert hat 😆

@marc2332 wahr, aber dann setzen Sie sich direkt potenziellen Angriffen durch diesen magischen Wrapper aus. Dies trifft möglicherweise nicht auf Ihr Szenario zu, ist jedoch auf jeden Fall ein Anliegen, das Sie berücksichtigen sollten.

@reZach Ich sehe keinen Grund, warum die window.require um ehrlich zu sein sicherer ist. Übrigens, es gibt schmutzigere Methoden.

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

@ marc2332 beide sind nicht sicher. Ich stelle nur fest, dass Sie sich auf diese Weise für Injektionsangriffe öffnen, wenn Sie Remote-Assets verwenden. 😄

Danke reZach! Ich denke, die Leute werden Ihren Ansatz früher verstehen

oder später

Mark Elphinstone
www.oxfordsourceltd.com

Hallo Zach, unterstützt die contextBridge Lösung Prototypen?

Mein ursprünglicher Code mit contextIsolation: false einfach eine Funktion an window angehängt. Diese Funktion gibt ein Promise<MediaStream> aber die Dokumentation von contextBridge sagt, dass Prototypen gelöscht werden und wenn ich das contextBridge erhalte ich ein leeres Objekt.

Gibt es eine Möglichkeit, Prototypen zu unterstützen, aber auch contextIsolation: true sicherzustellen?

Hallo Zach, unterstützt die contextBridge Lösung Prototypen?

Mein ursprünglicher Code mit contextIsolation: false einfach eine Funktion an window angehängt. Diese Funktion gibt ein Promise<MediaStream> aber die Dokumentation von contextBridge sagt, dass Prototypen gelöscht werden und wenn ich das contextBridge erhalte ich ein leeres Objekt.

Gibt es eine Möglichkeit, Prototypen zu unterstützen, aber auch contextIsolation: true sicherzustellen?

Nein, tut es nicht. Ich habe versucht, einen Kommentar von einem der Teammitglieder zu finden, an den ich mich erinnere, dass dies beabsichtigtes Verhalten ist, aber ich konnte nicht. Das Beste, was ich sagen kann, ist, dass in Electron v8 ( pr 20214 ) Änderungen an der Serialisierung von Objekten in IPC vorgenommen wurden (effektiv, wenn die Kontextisolierung aktiviert ist), die die Unterstützung für Funktionen (derzeit veraltet, wird in zukünftigen Versionen entfernt) einstellen oder Prototypen.

HINWEIS: Objekte, die nicht mit dem Structured Clone-Algorithmus von V8 serialisierbar sind, wie Funktionen, DOM-Objekte, spezielle Node/Electron-Objekte wie process.env oder WebContents, oder alle Objekte, die solche Elemente enthalten, werden mit dem alten base::Value- serialisiert. basierter Algorithmus. Dieses Verhalten ist jedoch veraltet und löst eine Ausnahme ab Electron 9 aus.

Dies ist wahrscheinlich nicht die Nachricht, die Sie hören wollten, aber ich hoffe, ich habe geholfen.

Hallo Zach, unterstützt die contextBridge Lösung Prototypen?
Mein ursprünglicher Code mit contextIsolation: false einfach eine Funktion an window angehängt. Diese Funktion gibt ein Promise<MediaStream> aber die Dokumentation von contextBridge sagt, dass Prototypen gelöscht werden und wenn ich das contextBridge erhalte ich ein leeres Objekt.
Gibt es eine Möglichkeit, Prototypen zu unterstützen, aber auch contextIsolation: true sicherzustellen?

Nein, tut es nicht. Ich habe versucht, einen Kommentar von einem der Teammitglieder zu finden, an den ich mich erinnere, dass dies beabsichtigtes Verhalten ist, aber ich konnte nicht. Das Beste, was ich sagen kann, ist, dass in Electron v8 ( pr 20214 ) Änderungen an der Serialisierung von Objekten in IPC vorgenommen wurden (effektiv, wenn die Kontextisolierung aktiviert ist), die die Unterstützung für Funktionen (derzeit veraltet, wird in zukünftigen Versionen entfernt) einstellen oder Prototypen.

HINWEIS: Objekte, die nicht mit dem Structured Clone-Algorithmus von V8 serialisierbar sind, wie Funktionen, DOM-Objekte, spezielle Node/Electron-Objekte wie process.env oder WebContents, oder alle Objekte, die solche Elemente enthalten, werden mit dem alten base::Value- serialisiert. basierter Algorithmus. Dieses Verhalten ist jedoch veraltet und löst eine Ausnahme ab Electron 9 aus.

Dies ist wahrscheinlich nicht die Nachricht, die Sie hören wollten, aber ich hoffe, ich habe geholfen.

Das ist schade, aber danke für deine schnelle Antwort.

Ich habe die oben beschriebenen Schritte befolgt (von @reZach und @aplum gegeben) und übergebe Informationen von meinem client.js an das preload.js an main.js .

Wenn alles lokal ausgeführt wird, funktioniert die Kommunikation, aber wenn ich electron-builder ausführe, bricht die Kommunikation ab. Beispielsweise funktioniert die Verwendung von setBadge() wunderbar, wenn es lokal ausgeführt wird. Ich kann die Zählung aus der Datei client.js entnehmen und sehen, wie sie an contextBridge und dann an main.js und die app.dock.setBadge(count) . Gibt es etwas, das mir bei der Kommunikation in Electron fehlt, nachdem es mit 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}`)
})

Preload.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)

Paket.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 , ich bin kein Betreuer des Elektronenteams, aber dies könnte eine bessere Frage für das Elektronenbuilder-Repository sein.

Ich neige eher zu der Annahme, dass es sich um ein Problem mit dem Code und nicht um einen Elektronengenerator handelt, wenn ich mir einen Moment Zeit nahm, um darüber nachzudenken.

Ich neige eher zu der Annahme, dass es sich um ein Problem mit dem Code und nicht um einen Elektronengenerator handelt, wenn ich mir einen Moment Zeit nahm, um darüber nachzudenken.

Ich bin mir sicher, dass mir ein Schlüsselstück fehlt, das dazu führen würde, dass alles wie erwartet funktioniert. Danke für Ihre Antwort. Ich habe ein Problem im electron-builder Repo eröffnet, aber es hat keine Anziehungskraft erhalten.

Ich neige eher zu der Annahme, dass es sich um ein Problem mit dem Code und nicht um einen Elektronengenerator handelt, wenn ich mir einen Moment Zeit nahm, um darüber nachzudenken.

Ich bin mir sicher, dass mir ein Schlüsselstück fehlt, das dazu führen würde, dass alles wie erwartet funktioniert. Danke für Ihre Antwort. Ich habe ein Problem im electron-builder Repo eröffnet, aber es hat keine Anziehungskraft erhalten.

Haben Sie mit Ihrem Problem ein MVP-Repository erstellt? Das hilft manchmal und dabei merkt man, was los ist.

@Amthieu und @CiriousJoker ich liebe euch beide danke.

  1. Garn hinzufügen -D anpassen-cra reagieren-app-rewired
  2. Ändern Sie package.json, um die App "rewired-app-rewired start" zu verwenden: "start": "BROWSER=none response-app-rewired start",
  3. Erstellen Sie die Datei 'config-overrides' im Stammverzeichnis der React-App:
    `const { override } = require('customize-cra');

Funktion addRendererTarget(config) {
config.target = 'Elektronen-Renderer';

Konfiguration zurückgeben;
}

module.exports = override(addRendererTarget);`

  1. Sie könnten jetzt { ipcRenderer} von 'electron' importieren :)

Ich verwende vuejs und füge einfach den Code in vue.config.js hinzu

module.exports = {
"transpileAbhängigkeiten": [
"vuetifizieren"
],
pluginOptionen: {
ElektronBuilder: {
KnotenIntegration: wahr
}
}
}

@genilsonmm Heiliger Mist, der funktioniert hat! Ich bin seit 3 ​​Stunden verrückt. Danke!!
Das spezifische Stück, das für mich funktioniert hat, war der
```javascript
pluginOptionen: {
ElektronBuilder: {
KnotenIntegration: wahr
}
},

"transpileAbhängigkeiten": [
"vuetifizieren"
],

das funktioniert bei mir nicht...aber window.require('electron') funktioniert gut

Für alle die das Problem haben "window.require ist keine Funktion"

Sie müssen ein Preoload-Skript auf Elektron erstellen.

  1. Erstellen Sie eine Datei namens preload.js in dem Verzeichnis, in dem Sie das Electron-Hauptskript haben. Fügen Sie diesen Code hinzu:

    window.require = erfordern;

  2. Gehen Sie zu Ihrem Elektron-Hauptskript und geben Sie dies in den Code ein, in dem Sie das Fenster erstellen:

    win = neues BrowserWindow({
    Breite: 1280,
    Höhe: 720,
    Webeinstellungen: {
    KnotenIntegration: falsch,
    preload: __dirname + '/preload.js'
    },

    })
    Damit laden Sie vor allem das Skript vor, dies hat das Problem für mich behoben. Ich hoffe auch für dich :)

@reZach deine Lösung hat bei mir funktioniert, aber ich habe einen Fehler bemerkt, über den andere Leute stolpern könnten (weil ich es getan habe, woops!):

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

Sie definieren Ihre Callback-Funktion als "func" , rufen dann aber "fn" auf, wenn Sie dies ändern, funktioniert es genau so, wie Sie es beschreiben.
Vielen Dank für den ausführlichen Beitrag :+1:

@genilsonmm Wenn ich vue.config.js schreibe:

pluginOptionen: {
ElektronBuilder: {
KnotenIntegration: wahr,

Ich erhalte die Fehlermeldung "erforderlich ist nicht definiert":

image

Wenn ich die Zeile "nodeIntegration: true" kommentiere, verschwindet die Fehlermeldung und die App funktioniert.

Was bedeutet das?

@marcoippolito Ich glaube, es bedeutet, dass der Webpack-Bundle-Code versucht, require was die Modulauflösungsmethode von node js ist. Da dies nicht verfügbar ist, wenn Sie die Knotenintegration deaktivieren, kann der Code nicht ausgeführt werden. Was Sie tun müssen, ist, Ihre Webpack-Konfiguration an Zielbrowser anzupassen ( var ist das Ziel, wenn ich mich richtig erinnere) und sicherstellen, dass keiner Ihrer Codes Node-APIs verwendet und der Fehler verschwinden sollte.

Für diejenigen, die TypeScript verwenden, gibt es ein gutes Beispiel in den offiziellen Next.js-Beispielen:
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 Heiliger Mist, der funktioniert hat! Ich bin seit 3 ​​Stunden verrückt. Danke!!
Das spezifische Stück, das für mich funktioniert hat, war der

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

正 解, 标记

Ich habe dieses Problem auch mit aktivierter NodeIntegration. Sowohl window.require als auch require funktionieren nicht.

+Dies passiert nur mit reagieren, nicht mit reinem Elektron

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

tengyifei picture tengyifei  ·  3Kommentare

chonsser picture chonsser  ·  3Kommentare

sindresorhus picture sindresorhus  ·  3Kommentare

reggi picture reggi  ·  3Kommentare

lealife picture lealife  ·  3Kommentare