Electron require () non è definito


107

Sto creando un'app Electron per i miei scopi. Il mio problema è che quando utilizzo le funzioni del nodo all'interno della mia pagina HTML viene generato un errore di:

'require ()' non è definito.

Esiste un modo per utilizzare le funzionalità del nodo in tutte le mie pagine HTML? Se è possibile, forniscimi un esempio di come farlo o fornisci un link. Ecco le variabili che sto cercando di utilizzare nella mia pagina HTML:

  var app = require('electron').remote; 
  var dialog = app.dialog;
  var fs = require('fs');

e questi sono i valori che sto usando in tutte le mie finestre HTML all'interno di Electron.


Risposte:


287

A partire dalla versione 5, l'impostazione predefinita per è nodeIntegrationcambiata da true a false. Puoi abilitarlo durante la creazione della finestra del browser:

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
});

Poiché la versione recente di Electron ha il valore predefinito di nodeIntegration su false per motivi di sicurezza, qual è il modo consigliato per accedere ai moduli del nodo? C'è un modo per comunicare con il processo principale senza nodeIntegration?
Paulo Henrique

28
@PauloHenrique nodeIntegration: trueè un rischio per la sicurezza solo quando esegui un codice remoto non attendibile sulla tua applicazione. Ad esempio, supponiamo che la tua applicazione apra una pagina web di terze parti. Ciò costituirebbe un rischio per la sicurezza perché la pagina Web di terze parti avrà accesso al runtime del nodo e può eseguire codice dannoso sul file system dell'utente. In tal caso ha senso impostare nodeIntegration: false. Se la tua app non mostra alcun contenuto remoto o mostra solo contenuto attendibile, l'impostazione nodeIntegration: trueva bene.
xyres

1
Questo mi stava facendo impazzire. La mia app semplicemente non mostrava errori e non eseguiva il mio codice. È stato quando ho usato un blocco try catch per intercettare l'errore che alla fine mi ha portato qui.
Heriberto Juarez

2
@PauloHenrique - Se vuoi seguire e creare un'app sicura (aderendo alle best practice di sicurezza), segui la mia configurazione come descrivo in questo commento: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

33

Per motivi di sicurezza, dovresti mantenere nodeIntegration: falsee utilizzare uno script di precaricamento per esporre proprio ciò di cui hai bisogno dall'API Node / Electron al processo di rendering (visualizzazione) tramite la variabile della finestra. Dai documenti di Electron :

Gli script di precaricamento continuano ad avere accesso ad requirealtre funzionalità di Node.js.


Esempio

main.js

const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: path.join(app.getAppPath(), 'preload.js')
  }
})

preload.js

const { remote } = require('electron');

let currWindow = remote.BrowserWindow.getFocusedWindow();

window.closeCurrentWindow = function(){
  currWindow.close();
}

renderer.js

let closebtn = document.getElementById('closebtn');

closebtn.addEventListener('click', (e) => {
  e.preventDefault();
  window.closeCurrentWindow();
});

1
Se sei un principiante dell'elettrone come me: il file renderer è solitamente incluso nell'html nel modo classico:<script src="./renderer.js"></script>
MrAn3

22

Spero che questa risposta riceva una certa attenzione, perché la grande maggioranza delle risposte qui lascia grandi buchi di sicurezza nella tua app elettronica. In effetti questa risposta è essenzialmente ciò che dovresti fare per usarla require()nelle tue app di elettroni. (C'è solo una nuova API elettronica che lo rende un po 'più pulito nella v7).

Ho scritto una spiegazione / soluzione dettagliata in GitHub usando le API elettroniche più recenti di come puoi fare require()qualcosa, ma spiegherò brevemente qui perché dovresti seguire un approccio usando uno script di precaricamento, contextBridge e ipc.

Il problema

Le app di Electron sono fantastiche perché possiamo usare il nodo, ma questo potere è un'arma a doppio taglio. Se non stiamo attenti, diamo a qualcuno l'accesso a node tramite la nostra app, e con node un cattivo attore può corrompere la tua macchina o cancellare i file del tuo sistema operativo (tra le altre cose, immagino).

Come indicato da @raddevus in un commento, questo è necessario quando si caricano contenuti remoti . Se la tua app elettronica è completamente offline / locale , probabilmente stai semplicemente accendendola nodeIntegration:true. Tuttavia, sceglierei comunque di continuare nodeIntegration:falsea fungere da salvaguardia per utenti accidentali / malintenzionati che utilizzano la tua app e impedire a qualsiasi possibile malware che potrebbe essere installato sulla tua macchina di interagire con la tua app di elettroni e utilizzare il nodeIntegration:truevettore di attacco (incredibilmente raro , ma potrebbe succedere)!

Che aspetto ha il problema

Questo problema si manifesta quando tu (uno qualsiasi dei seguenti):

  1. Hanno nodeIntegration:trueabilitato
  2. Usa il remotemodulo

Tutti questi problemi danno accesso ininterrotto al nodo dal processo di rendering. Se il processo di rendering viene mai dirottato, puoi considerare che tutto è perduto.

Qual è la nostra soluzione

La soluzione è di non dare al renderer l' accesso diretto al nodo (cioè require()), ma di dare al nostro processo principale di electron l'accesso a require, e ogni volta che il nostro processo di rendering ha bisogno di usare require, marshalling una richiesta al processo principale.

Il modo in cui funziona nelle ultime versioni (7+) di Electron è che sul lato renderer abbiamo impostato i collegamenti ipcRenderer e sul lato principale abbiamo impostato i collegamenti ipcMain . Nelle associazioni ipcMain impostiamo metodi di ascolto che utilizzano i moduli che noi require(). Questo va bene e va bene perché il nostro processo principale può requiretutto ciò che vuole.

Usiamo il contextBridge per passare le associazioni ipcRenderer al codice della nostra app (da usare), e così quando la nostra app deve usare i requiremoduli d in main, invia un messaggio tramite IPC (inter-process-communication) e il processo principale viene eseguito un po 'di codice, quindi inviamo un messaggio con il nostro risultato.

Approssimativamente , ecco cosa vuoi fare.

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>

Disclaimer

Sono l'autore di secure-electron-template, un modello sicuro per creare app di elettroni. Ci tengo a questo argomento e ci sto lavorando da alcune settimane (in questo momento).


Sono un nuovo sviluppatore di ElectronJS e stavo lavorando a un tutorial PluralSite che non viene più eseguito poiché le impostazioni di integrazione del nodo sono state modificate. Il tuo post è molto buono e sto anche leggendo il tuo post Github associato e i documenti di sicurezza ufficiali di Electron associati. Ottenere l'integrazione del nodo impostata esattamente correttamente (in modo che le app funzionino correttamente e siano sicure) ha molte parti mobili (specialmente per i principianti). Seguendo la frase di Electron docs "È fondamentale non abilitare l'integrazione di Node.js in nessun renderer (BrowserWindow, BrowserView o <webview>) che carica contenuto remoto. " (Mia enfasi)
raddevus

Presumo che l'inverso sia vero ... "se BrowserWindow non carica il contenuto remoto, allora è sicuro includere l'integrazione del nodo". Se quella frase è vera potresti voler modificare un po 'i tuoi post per enfatizzare questo punto perché nel mio caso ho due app che rientrano in quella categoria e non ho bisogno di rimuovere l'integrazione dei nodi. Grazie per l'aiuto.
raddevus

1
@raddevus Grazie, spero che il modello ti aiuti a creare app elettroniche sicure (se scegli di usarle)! Sì, hai ragione sulla tua enfasi. Tuttavia, dirò che la disabilitazione nodeIntegrationimpedisce all'utente di causare danni accidentalmente o intenzionalmente a se stesso durante l'utilizzo dell'app, ed è una protezione extra nel caso in cui alcuni malware si siano attaccati al tuo processo elettronico e siano stati in grado di eseguire XSS sapendo che questo vettore era aperto (incredibilmente raro, ma è lì che è andato il mio cervello)!
Zac

1
@raddevus Grazie, sto aggiornando i miei post per riflettere il tuo commento.
Zac

9

Stai usando nodeIntegration: falsedurante l'inizializzazione di BrowserWindow? In tal caso, impostalo su true(il valore predefinito è true).

E includi i tuoi script esterni nell'HTML in questo modo (non come <script> src="./index.js" </script>):

<script>
   require('./index.js')
</script>

Sto usando pdf js offline con questo.Quindi quando sto usando nodeIntegration: true allora PDFJS.getDocument non è un errore di funzione verrà arrivato.Come impostare nodeIntegration: true nella mia pagina html quando pdfjs è completamente caricato.
Mari Selvan

Hai guardato questo esempio ? Potresti essere in grado di importare il pacchetto tramite var pdfjsLib = require('pdfjs-dist')e usarlo in questo modo.
RoyalBingBong

Perché mi consigliate di utilizzare al requireposto di <script src="..."></script>? Anche questo ha una domanda senza risposta qui .
bluenote10

@ bluenote10 Webpack risponde a questa domanda : è difficile dire da cosa dipende uno script, l'ordine di dipendenza deve essere gestito e il codice non necessario verrà comunque scaricato ed eseguito.
haykam

7

Prima di tutto, la soluzione @Sathiraumesh lascia la tua applicazione elettronica con enormi problemi di sicurezza. Immagina che la tua app stia aggiungendo alcune funzionalità extra messenger.com, ad esempio l'icona della barra degli strumenti cambierà o lampeggerà quando hai un messaggio non letto. Quindi nel tuo main.jsfile, crei una nuova BrowserWindow in questo modo (nota che ho scritto intenzionalmente in modo sbagliato messenger.com):

app.on('ready', () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
    mainWindow.loadURL(`https://messengre.com`);
});

E se messengre.comfosse un sito web dannoso, che vuole danneggiare il tuo computer. Se imposti nodeIntegration: truequesto sito ha accesso al tuo file system locale e può eseguirlo:

require('child_process').exec('rm -r ~/');

E la tua directory home è sparita.

Soluzione
Esponi solo ciò di cui hai bisogno, invece di tutto. Ciò si ottiene precaricando il codice javascript con requireistruzioni.

// main.js
app.on('ready', () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            preload: `${__dirname}/preload.js`
        }
    });
    mainWindow.loadURL(`https://messengre.com`);
});
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// index.html
<script>
    window.ipcRenderer.send('channel', data);
</script>

Ora terribile messengre.comnon può eliminare l'intero file system.


-1

Alla fine l'ho fatto funzionare: aggiungi questo codice al tuo documento HTML Script Element.

Ci scusiamo per la risposta tardiva, utilizzo il codice seguente per eseguire questa operazione.

window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;

E usa nodeRequireinvece di usare require.

Funziona bene.


Si prega di condividere il codice della pagina HTML.
Vijay

-1

Devi abilitare nodeIntegration in webPreferences per usarlo. vedi sotto,

const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
})
win.show()

C'era una rottura cambiamenti API elettroni 5.0 ( Annuncio sul Repository ). Nelle versioni recenti nodeIntegration è impostato su false per impostazione predefinita .

Documenti A causa dell'integrazione Node.js di Electron, ci sono alcuni simboli extra inseriti nel DOM come modulo, export, require. Ciò causa problemi ad alcune librerie poiché vogliono inserire i simboli con lo stesso nome.Per risolvere questo problema, puoi disattivare l'integrazione dei nodi in Electron:

Ma se vuoi mantenere le capacità di usare Node.js e le API Electron, devi rinominare i simboli nella pagina prima di includere altre librerie:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.