Requer elétron () não está definido


107

Estou criando um aplicativo Electron para meu próprio propósito. Meu problema é que quando estou usando funções de nó dentro da minha página HTML, ocorre um erro de:

'require ()' não está definido.

Existe alguma maneira de usar as funcionalidades do Node em todas as minhas páginas HTML? Se for possível, dê-me um exemplo de como fazer isso ou forneça um link. Aqui estão as variáveis ​​que estou tentando usar na minha página HTML:

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

e esses são os valores que estou usando em todas as minhas janelas HTML no Electron.


Respostas:


287

A partir da versão 5, o padrão para nodeIntegrationmudou de verdadeiro para falso. Você pode ativá-lo ao criar a janela do navegador:

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

Como a versão recente do Electron tem o default do nodeIntegration como false devido a razões de segurança, qual é a maneira recomendada de acessar os módulos do nó? Existe uma maneira de se comunicar com o processo principal sem nodeIntegration?
Paulo Henrique

28
@PauloHenrique nodeIntegration: trueé um risco de segurança apenas quando você está executando algum código remoto não confiável em seu aplicativo. Por exemplo, suponha que seu aplicativo abra uma página da web de terceiros. Isso seria um risco de segurança porque a página da Web de terceiros terá acesso ao tempo de execução do nó e pode executar algum código malicioso no sistema de arquivos do usuário. Nesse caso, faz sentido definir nodeIntegration: false. Se seu aplicativo não estiver exibindo nenhum conteúdo remoto ou exibindo apenas conteúdo confiável, a configuração nodeIntegration: trueestá correta.
xyres

1
Isso estava me deixando louco. Meu aplicativo simplesmente não mostrava nenhum erro e não executava meu código. Foi quando usei um bloco try catch para interceptar o erro que finalmente me trouxe aqui.
Heriberto Juarez

2
@PauloHenrique - Se você deseja seguir e criar um aplicativo seguro (aderindo às melhores práticas de segurança), siga minha configuração conforme descrevo neste comentário: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

33

Por motivos de segurança, você deve manter nodeIntegration: falsee usar um script de pré-carregamento para expor apenas o que você precisa da API Node / Electron para o processo de renderização (visualização) por meio da variável de janela. Dos documentos do Electron :

Os scripts de pré-carregamento continuam tendo acesso a requireoutros recursos do Node.js.


Exemplo

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 você for um novato em elétrons como eu: o arquivo renderizador é geralmente incluído no html da maneira clássica:<script src="./renderer.js"></script>
MrAn3

22

Espero que esta resposta receba alguma atenção, porque a grande maioria das respostas aqui deixa grandes brechas de segurança em seu aplicativo de elétrons. Na verdade, essa resposta é essencialmente o que você deve fazer para usar require()em seus aplicativos de elétrons. (Existe apenas uma nova API de elétrons que o torna um pouco mais limpo na v7).

Eu escrevi uma explicação / solução detalhada no github usando as apis de elétrons mais atuais de como você pode fazer require()algo, mas vou explicar brevemente aqui porque você deve seguir uma abordagem usando um script de pré-carregamento, contextBridge e ipc.

O problema

Aplicativos de elétrons são ótimos porque podemos usar o node, mas esse poder é uma faca de dois gumes. Se não tomarmos cuidado, damos a alguém acesso ao node por meio de nosso aplicativo, e com o node um malfeitor pode corromper sua máquina ou deletar seus arquivos de sistema operacional (entre outras coisas, imagino).

Conforme mencionado por @raddevus em um comentário, isso é necessário ao carregar conteúdo remoto . Se o seu aplicativo eletrônico é totalmente offline / local , provavelmente você não terá problemas simplesmente ligando-o nodeIntegration:true. Ainda assim, optaria por continuar nodeIntegration:falsea agir como uma proteção para usuários acidentais / mal-intencionados que usam seu aplicativo e evitar que qualquer malware que possa ser instalado em sua máquina interaja com seu aplicativo de elétrons e use o nodeIntegration:truevetor de ataque (incrivelmente raro , mas pode acontecer)!

Como é o problema?

Este problema se manifesta quando você (qualquer um dos abaixo):

  1. Ter nodeIntegration:true ativado
  2. Use o remote módulo

Todos esses problemas dão acesso ininterrupto ao nó do seu processo de renderização. Se o seu processo de renderização for sequestrado, você pode considerar que tudo está perdido.

Qual é a nossa solução

A solução é não dar ao renderizador acesso direto ao nó (isto é require()), mas dar ao nosso processo principal de elétrons acesso require, e a qualquer momento que nosso processo renderizador precisar usarrequire , empacotar uma solicitação para o processo principal.

A maneira como isso funciona nas versões mais recentes (7 +) do Electron é no lado do renderizador que configuramos os vínculos ipcRenderer e, no lado principal, configuramos os vínculos ipcMain . Nas ligações ipcMain, configuramos métodos de escuta que usam módulos nós require(). Isso é bom e bom porque nosso processo principal poderequire tudo o que quiser.

Usamos o contextBridge para passar as ligações ipcRenderer para o código do nosso aplicativo (para usar) e, portanto, quando nosso aplicativo precisa usar os requiremódulos d no principal, ele envia uma mensagem via IPC (comunicação entre processos) e o processo principal é executado algum código e, em seguida, enviamos uma mensagem de volta com nosso resultado.

Aproximadamente , aqui está o que você deseja fazer.

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>

aviso Legal

Sou o autor de secure-electron-template, um modelo seguro para construir aplicativos de elétrons. Preocupo-me com este tópico e venho trabalhando nisso há algumas semanas (neste momento).


Sou um novo desenvolvedor de ElectronJS e estava trabalhando em um tutorial do PluralSite que não funciona mais porque as configurações de integração do nó foram alteradas. Sua postagem é muito boa e também estou lendo sua postagem associada no Github e os documentos oficiais de segurança do Electron associados. Fazer com que a integração do nó seja configurada exatamente da maneira certa (para que os aplicativos funcionem corretamente e sejam seguros) tem muitas partes móveis (especialmente para novatos). Seguindo a frase dos documentos Electron "É fundamental que você não habilite a integração Node.js em qualquer renderizador (BrowserWindow, BrowserView ou <webview>) que carregue conteúdo remoto. " (
Grifo

Estou assumindo que o inverso é verdadeiro ... "se o BrowserWindow não carregar o conteúdo remoto, é seguro incluir a integração do Node". Se essa frase for verdadeira, você pode querer alterar um pouco suas postagens para enfatizar esse ponto, porque, no meu caso, tenho dois aplicativos que se enquadram nessa categoria e não precisam remover a integração do nó. Obrigado pela ajuda.
raddevus

1
@raddevus Obrigado, espero que o modelo ajude você a construir aplicativos eletrônicos seguros (se você decidir usá-lo)! Sim, você está correto em sua ênfase. No entanto, direi que a desativação nodeIntegrationevita que o usuário acidentalmente ou propositalmente causem danos a si mesmo ao usar o aplicativo e é uma proteção extra no caso de algum malware ser anexado ao seu processo eletrônico e ser capaz de executar XSS sabendo que este vetor estava aberto (incrivelmente raro, mas é para lá que meu cérebro foi)!
Zac

1
@raddevus Obrigado, estou atualizando minhas postagens para refletir seu comentário.
Zac

9

Você está usando nodeIntegration: falsedurante a inicialização do BrowserWindow? Em caso afirmativo, defina-o como true(o valor padrão é true).

E inclua seus scripts externos no HTML como este (não como <script> src="./index.js" </script>):

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

Estou usando o pdf js offline com this.So quando estou usando nodeIntegration: true então PDFJS.getDocument não é um erro de função que vai chegar.Como definir nodeIntegration: true na minha página html quando o pdfjs está completamente carregado.
Mari Selvan

Você já viu este exemplo ? Você pode apenas importar o pacote via var pdfjsLib = require('pdfjs-dist')e usá-lo desta forma.
RoyalBingBong

Por que você recomenda usar em requirevez de <script src="..."></script>? Isso também tem uma questão sem resposta aqui .
bluenote10

@ bluenote10 Webpack responde a esta pergunta : é difícil dizer do que um script depende, a ordem de dependência deve ser gerenciada e o código desnecessário ainda será baixado e executado.
haykam

7

Em primeiro lugar, a solução @Sathiraumesh deixa seu aplicativo eletrônico com um grande problema de segurança. Imagine que seu aplicativo está adicionando alguns recursos extras messenger.com, por exemplo, o ícone da barra de ferramentas muda ou pisca quando você tem uma mensagem não lida. Portanto, em seu main.jsarquivo, você cria uma nova BrowserWindow assim (observe que escrevi messenger.com de forma incorreta intencionalmente):

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

E se messengre.comfor um site malicioso, que quer danificar o seu computador. Se você definir que nodeIntegration: trueeste site tem acesso ao seu sistema de arquivos local e pode executar isto:

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

E seu diretório inicial sumiu.

Solução
Exponha apenas o que você precisa, em vez de tudo. Isso é conseguido através do pré-carregamento do código javascript com requireinstruções.

// 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>

Agora o terrível messengre.comnão pode excluir todo o seu sistema de arquivos.


-1

Finalmente, fiz funcionar. Adicione este código ao elemento de script do documento HTML.

Desculpe pelo atraso Reply.I uso o código abaixo para fazer isso.

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

E use em nodeRequirevez de usar require.

Funciona bem.


Compartilhe o código da sua página HTML.
Vijay

-1

Você deve habilitar o nodeIntegration em webPreferences para usá-lo. ver abaixo,

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

Houve uma quebra de alterações da API no elétron 5.0 ( Anúncio no Repositório ). Em versões recentes nodeIntegration é, por padrão, definido como false .

Docs Devido à integração Node.js do Electron, existem alguns símbolos extras inseridos no módulo DOM como, exportações, requerem. Isso causa problemas para algumas bibliotecas, pois elas desejam inserir os símbolos com os mesmos nomes. Para resolver isso, você pode desativar a integração do nó no Electron:

Mas se você deseja manter a capacidade de usar APIs Node.js e Electron, é necessário renomear os símbolos na página antes de incluir outras bibliotecas:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.