Electron: Erro ao importar elétron na reação | importar {ipcRenderer} de 'electron'

Criado em 3 jul. 2017  ·  65Comentários  ·  Fonte: electron/electron

Eu criei um aplicativo de reação simples com criar-reagir-aplicativo e eu o integrei com o elétron com sucesso. Tudo estava funcionando bem até que tentei importar elétron para dentro do arquivo do criador de ação. Se eu remover a linha abaixo, o aplicativo funcionará bem. O problema é que não consigo usar o ipcRenderer para me comunicar do lado da reação com o processo principal do elétron.

Esta linha faz com que o aplicativo falhe:
import { ipcRenderer } from 'electron';

Estou tendo o erro a seguir:

TypeError: fs.existsSync não é uma função
(função anônima)
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')

Descobri no Google que esse é um problema comum ao tentar importar elétron.

Obrigado pela ajuda

Comentários muito úteis

@MarshallOfSound meu erro.

Eu encontrei a solução no problema nº 7300 se ela puder ajudar alguém.

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

Observe que isso funcionará quando você executar o aplicativo Electron, mas se quiser apenas testar seu código React dentro do navegador, ele ainda travará (window.require não está definido no navegador, pois está no Electron).

Todos 65 comentários

CRA usa webpack que mexe com o carregamento de módulo padrão (incluindo fs).

Eu recomendo olhar para o modo Electron para webpack e ejetar do CRA

Os problemas do GitHub são para solicitações de recursos e relatórios de bugs, perguntas sobre o uso do Electron devem ser direcionadas à comunidade ou ao Canal Slack .

@MarshallOfSound meu erro.

Eu encontrei a solução no problema nº 7300 se ela puder ajudar alguém.

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

Observe que isso funcionará quando você executar o aplicativo Electron, mas se quiser apenas testar seu código React dentro do navegador, ele ainda travará (window.require não está definido no navegador, pois está no Electron).

Se você deseja acessar app.quit (), você pode usar isto:

const {app} = window.require ('elétron'). remoto;

Talvez ajude alguém ...

@CiriousJoker essas são as soluções, obrigado!

Ainda estou recebendo window.require is not a function . Estou usando o Electron com React Starter Kit (https://github.com/kriasoft/react-starter-kit). Tudo está funcionando bem, exceto isso.

Eu configurei meu aplicativo Electron para carregar meu aplicativo da web, então o aplicativo não está sendo executado localmente:
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6

O que estou tentando fazer é chamar ipcRenderer em um dos meus arquivos React. Não tenho certeza se isso é possível quando meu aplicativo está sendo carregado da web. Alguma sugestão?

@holgersindbaek

No mesmo barco que você ... Você encontrou uma solução?

Não. Tenho quase certeza de que não é possível carregar o ipcRenderer a partir do navegador.

Se você estiver executando o aplicativo React no navegador, ele não funcionará. Execute-o dentro do Electron e você ficará bem.

@Amthieu Obrigado pelo conselho. Ainda estou em dúvida sobre como posso fazer meu projeto React (baseado no React Starter Kit) rodar em Electron. Qualquer conselho seria muito apreciado:

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

Certo, eu tenho uma solução.

1) Crie um preload.js file com o código:

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

2) Pré-carregue este arquivo em seu main.js via webPreferences :

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

3) Agora, você terá acesso a partir de seu aplicativo de reação. Por exemplo, isso vai funcionar:

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

Observação - usando: https://github.com/cheton/is-electron para a função isElectron()

@HemalR A Etapa 3 deve ser a seguinte (agora):

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

Nota : window.isElectron não é uma função.

@ nparsons08

Desculpas - deveria ter adicionado de onde estou obtendo isElectron, editado meu exemplo de código com o link para: https://github.com/cheton/is-electron

@holgersindbaek
Existe uma solução agora

para mim só funciona se nodeIntegration for true ;

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

funciona muito bem a solução @HemalR !

agora COMO enviar DO elétron PARA Reagir?

tentei com
no lado do elétron

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

mas nada foi recebido do ouvinte do React

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

é correto usar ipcMain.emit () ou devo usar outra coisa?

ok acabei de descobrir que tenho que usar (no processo principal do elétron)

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

obrigado a todos!

Eu tentei todos os itens acima sem sucesso. O que funcionou para mim foi um hack gigante. Modifique o arquivo ./node_modules/electron/index.js e codifique o caminho para electron.exe

por exemplo

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

module.exports = getElectronPath();

Uau, não consegui fazer o IPCRenderer funcionar nos meus componentes React. Eu tentei todos os métodos acima. Algum de vocês, por acaso, deu alguma dica que eu posso usar para que funcione? obrigado

Hmmm ... Meu aplicativo de elétrons ainda funciona bem com a solução acima - mas não o atualizo há alguns meses (não precisava).

Eu me pergunto se há uma alteração significativa que impediria isso de funcionar. Talvez vocês possam postar suas versões de elétrons?

@cyclonstep Há algum erro específico que você está recebendo? É difícil ajudar sem um snippet de código ou alguns logs ...

Estou usando o pacote para empacotamento.
Window.require também resolveu para mim (também mostrando, o que não fez):

importar Vue de 'vue / dist / vue.min'
importar aplicativo de './App'

// RUIM? importar {ipcRenderer} de 'electron'
// RUIM? const {ipcRenderer} = requer ('elétron')
// BOA:
const {ipcRenderer} = window.require ('electron')

(mais abaixo no mesmo arquivo está o elétron „pong-Demo“ , que prova que funciona)

Talvez seja digno de nota: mesmo quando errado, o tamanho do pacote não aumenta (compare com sem a necessidade de elétrons. Esta é até agora minha primeira e até agora única importação de elétrons do lado de renderização) em todo o tamanho do elétron ou algo assim, mas apenas em torno de 20 kb , que parece ser algum código de shim / wrapper por conta própria, vindo 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

Enfim, as coisas funcionam como disse acima.

Node versão 10.2.0
Chrome versão 66.0.3359.181
Electron versão 3.0.2

window.require não estava funcionando para mim em meu script principal com o erro window is not defined , então mudei para const electron = eval('require')("electron") . Espero que isso ajude alguém. Usando o webpack, e o problema era que o webpack estava avaliando minha instrução require no momento da compilação.

@MarshallOfSound meu erro.

Eu encontrei a solução no problema nº 7300 se ela puder ajudar alguém.

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

Observe que isso funcionará quando você executar o aplicativo Electron, mas se quiser apenas testar seu código React dentro do navegador, ele ainda travará (window.require não está definido no navegador, pois está no Electron).

E para texto datilografado:

import {IpcRenderer} from 'electron';

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

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

@moshfeu Sua solução funciona de forma fantástica. Não preciso do Webpack ou do Browserfy para usar o IpcRenderer no meu projeto React. Muito obrigado novamente: D

Para texto datilografado e usando o exemplo de nodeIntegration: true : https://github.com/electron/electron/issues/9920#issuecomment -336757899:

Certo, eu tenho uma solução.

  1. Crie um preload.js file com o código:
window.ipcRenderer = require('electron').ipcRenderer;
  1. Pré-carregue este arquivo em seu main.js via webPreferences :
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });
  1. Agora, você terá acesso a partir de seu aplicativo de reação. Por exemplo, isso vai funcionar:
componentDidMount() {
      if (isElectron()) {
          console.log(window.ipcRenderer);
          window.ipcRenderer.on('pong', (event, arg) => {
              this.setState({ipc: true})
          })
          window.ipcRenderer.send('ping')
      }
  }

Observação - usando: https://github.com/cheton/is-electron para a função isElectron()

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

Eu usei isto:

import { IpcRenderer } from 'electron';

declare global {
  interface Window {
    ipcRenderer: IpcRenderer
  }
}

export const { ipcRenderer } = window;

Espero que ajude alguém por aí! Funciona com stenciljs, e imagino reagir e angular

basta adicionar target: "electron-renderer" nas configurações do webpack.
export default {
...
alvo: "renderizador de elétrons"
...
}

Olá, eu uso o CRA + Electron. E eu construo meu projeto por 'renderizador de elétrons' alvo. Funciona bem ao carregar arquivos na pasta de compilação, mas gera um erro quando desenvolvo e carrego o url localhost:3000 . Acho que o motivo é que eu uso a API do nó e a API do elétron nos componentes de reação. Alguém tem a solução? Obrigado.

TypeError: fs.existsSync is not a function

Sim, a fs api não está disponível no navegador. Se eu entendi a situação corretamente, você executa seu aplicativo usando react-scripts start (que é o script padrão de npm start no CRA).

A maneira certa de fazer isso é executar electron . . Você pode vê-lo nos documentos: https://electronjs.org/docs/tutorial/first-app.

LMK se funcionar para você. Você também pode ver como funciona em meu aplicativo - https://github.com/moshfeu/y2mp3 (observe que não foi criado com CRA)

Olá, eu uso o CRA + Electron. E eu construo meu projeto por 'renderizador de elétrons' alvo. Ele funciona bem ao carregar arquivos na pasta de compilação, mas gera um erro quando eu desenvolvo e carrego o url localhost:3000 . Acho que o motivo é que eu uso a API do nó e a API do elétron nos componentes de reação. Alguém tem a solução? Obrigado.

TypeError: fs.existsSync is not a function

basta adicionar target: "electron-renderer" nas configurações do webpack.
export default {
...
alvo: "renderizador de elétrons"
...
}

Funciona pra mim também.

Aqui está minha versão de ferramentas:

  • webpack: "3.8.1"
  • elétron: "4.2.1"

Acho que electron-renderer apenas para resolver esse problema do elétron. Não preciso usar a sintaxe com fio como window.required , e até perdi a digitação!

Para mim, funcionou definindo o alvo: "renderização de elétrons" para webpack e nodeIntegration: true nas opções do BrowserWindow
Estou usando webpack + electron + react das últimas versões de hoje

Estou executando o aplicativo criar-reagir com TypeScript e Electron. Eu segui essas instruções muito boas para uma configuração. Mas então eu também encontrei este erro. A solução que funciona para mim é a soma das coisas ditas neste tópico:

  1. Faça o react-script usar "electron-renderer" como target . Eu uso rescripts com rescript-env para isso.

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

E finalmente em seu aplicativo

App.tsx

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

@LukasBombach
Isso também deve funcionar:

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

Então você terá que digitar as constantes necessárias

Obrigado!

Aqui estão as etapas que usei para o Vue, caso seja útil para outra pessoa.

Certifique-se de que a integração do nó esteja ativada para o processo do renderizador, incluindo-o nas preferências da web ao criar uma janela do navegador:

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

Importe o que você precisa em seu aplicativo. Estou importando o módulo shell para o meu componente assim:

import { shell } from 'electron'

Para qualquer pessoa que ainda esteja com o mesmo problema. Esta é a melhor solução que encontrei até agora

`` `js
novo BrowserWindow ({
webPreferences: {
nodeIntegration: true
}
});

Espero que este comentário seja notado, porque muitas pessoas estão perguntando sobre a importação de fs ou ipcRenderer em seus aplicativos. É uma necessidade comum para aplicativos de elétrons, mas descobri que muitas pessoas não entenderam direito e estão usando padrões desatualizados. tl; dr - há uma vulnerabilidade de segurança se você não importar seu módulo de nó (ou seja, fs ) ou módulo eletrônico (ou seja, ipcRenderer ) da maneira correta. Se estiver usando seu aplicativo apenas para você, você está _provavelmente_ seguro, mas se quiser compartilhar ou vender seu aplicativo, leia com atenção.

Nosso objetivo

Antes de entrar na solução, é importante entender _por que_ estamos fazendo isso em primeiro lugar. Os aplicativos de elétrons nos permitem incluir módulos de nó em nossos aplicativos, o que lhes dá um poder incrível, mas preocupações de segurança. Queremos permitir que nosso aplicativo use recursos do sistema operacional nativo (ou seja, nó), mas não queremos que eles sejam abusados .

Conforme mencionado por nodeIntegration:true . Ainda assim, optaria por manter nodeIntegration:false para agir como uma proteção para usuários acidentais / mal-intencionados que usam seu aplicativo e evitar que qualquer possível malware que possa ser instalado em sua máquina interaja com seu aplicativo de elétron e use o vetor de ataque nodeIntegration:true (incrivelmente raro, mas pode acontecer)!

O caminho fácil

Definir nodeIntegration: true em seu BrowserWindow dá ao seu processo de renderização acesso aos módulos de nó. Fazer _isso_ é vulnerável. Você tem acesso a require("fs") e require("electron") , mas isso significa que se alguém encontrar uma vulnerabilidade XSS, poderá executar qualquer comando que você expôs em seu processo de renderização.

Pense em deletar todos os arquivos do seu computador ou qualquer outra coisa que seja realmente ruim.

A maneira (alternativa) fácil

Além de definir nodeIntegration como true, é provável que seu aplicativo esteja usando webpack para agrupar arquivos de aplicativo. Webpack bagunça certos símbolos, então configurações como target: 'electron-renderer' ou webpack externos permitem que você passe por essas variáveis ​​( ipcRenderer ) em seu aplicativo.

Ainda assim, isso não muda nada, exceto como você está configurando seu aplicativo.

A maneira (outra alternativa) fácil

Você pode usar o módulo remoto que dá acesso a ipcRenderer . É basicamente 'A maneira mais fácil' de uma forma diferente. Não é recomendado pelas

Ou seja, usar o controle remoto pode permitir que alguém modifique o protótipo de um js-object e destrua sua máquina / aplicativo.

O caminho _quase_ certo

@marksyzm tem uma solução melhor , embora não perfeita, onde usamos IPC para enviar ipcRenderer para o processo de renderização. Este tipo de configuração também é vulnerável a ataques de poluição de protótipo . Se você quiser que seu aplicativo esteja 80% do caminho, eu usaria este método, pois provavelmente não exigirá muita refatoração.

Caminho certo

A maneira certa de importar seu fs / ipcRenderer para o seu processo de renderização é com IPC (comunicação entre processos). Esta é a maneira que o Electron tem de permitir que você converse entre o processo principal e o do renderizador. Detalhado, é assim que seu aplicativo deve ser:

  1. Um BrowserWindow tem uma propriedade preload . Esta propriedade é um arquivo js que carrega com acesso a require (o que significa que você pode exigir ipcRenderer)
  2. Seu BrowserWindow também terá contextIsolation: true para evitar ataques de poluição de protótipo, mas isso significa que você precisa usar o contextBridge para passar o ipcRenderer para seu processo de renderização
  3. Usando o script de pré-carregamento e o contextBridge, você permite que seu processo de renderização acesse o ipcRenderer
  4. Em seu script principal, você cria ouvintes para o ipcRenderer (no módulo ipcMain ). Dentro desses ouvintes, você pode usar o módulo fs

_Aproximadamente_ é assim que todas essas etapas se parecem:

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>

No mínimo, eu _acredito_ que você precisa do elétron v7 para esses recursos.

Como eu sei disso?

Preocupo-me com aplicativos eletrônicos seguros e construí secure-electron-template para criar um modelo de aplicativo eletrônico para preparar a segurança em vez de pensar na segurança como uma reflexão tardia.

Com base no comentário de @reZach acima, estou fazendo algo como o seguinte. A principal diferença está no preload.js , onde minha API está mais próxima da do 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();
}

E para aqueles que usam 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;
}

Alguém sabe por que estou recebendo o erro de módulo não encontrado quando tento colocar isso no preload.js?

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

Obrigado

@silentlight, isso não está relacionado ao elétron, talvez você tenha um repositório github onde a conversa possa continuar? (Parece que seu caminho em require() não está correto e é por isso que está gerando um erro, sem ver mais código).

Embora já exista a solução window.require , vou compartilhar a minha, apenas por diversão:

function magic(module){
    require(module)
}

const { ipcRenderer } = magic('electron')

Seus empacotadores não se identificarão enquanto você está importando um módulo, pois você não está usando módulos ES ou sintaxe CommonJS, então ele não tentará empacotá-lo e, portanto, não gerará um erro

Eu me pergunto por que ninguém tentou isso ainda 😆

@ marc2332 true, mas então você se expõe diretamente a ataques potenciais por esse invólucro mágico. Pode não se aplicar ao seu cenário, mas definitivamente é uma preocupação que você deve considerar.

@reZach Não vejo motivos para que o window.require seja mais seguro para ser honesto. Aliás, existem maneiras mais sujas de fazer isso.

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

@ marc2332 também não são seguros. Estou apenas observando, se você usar ativos remotos, estará se abrindo para ataques de injeção dessa forma. 😄

Obrigado reZach! Acho que as pessoas entenderão sua abordagem mais cedo

ou mais tarde

Mark Elphinstone
www.oxfordsourceltd.com

Olá Zach, a solução contextBridge oferece suporte a protótipos?

Meu código original com contextIsolation: false simplesmente anexou uma função em window . Esta função retorna Promise<MediaStream> porém a documentação de contextBridge diz que os protótipos foram descartados e quando usei contextBridge recebo um objeto vazio.

Existe uma maneira de oferecer suporte a protótipos, mas também garantindo contextIsolation: true ?

Olá Zach, a solução contextBridge oferece suporte a protótipos?

Meu código original com contextIsolation: false simplesmente anexou uma função em window . Esta função retorna Promise<MediaStream> porém a documentação de contextBridge diz que os protótipos foram descartados e quando usei contextBridge recebo um objeto vazio.

Existe uma maneira de oferecer suporte a protótipos, mas também garantindo contextIsolation: true ?

Não, não importa. Eu estava tentando encontrar um comentário de um dos membros da equipe de que me lembro que esse era o comportamento pretendido, mas não consegui. O melhor que posso dizer é no Electron v8, ( pr 20214 ) trouxe mudanças para a serialização de objetos dentro do IPC (efetivamente quando o isolamento de contexto está ativado) que irá descartar o suporte (atualmente obsoleto, será removido em versões futuras) para funções ou protótipos.

NOTA: Objetos que não são serializáveis ​​com o algoritmo Structured Clone do V8, como funções, objetos DOM, objetos Node / Electron especiais como process.env ou WebContents, ou quaisquer objetos que contenham tais itens serão serializados com a base antiga :: Value- algoritmo baseado. No entanto, esse comportamento está obsoleto e lançará uma exceção começando com o Electron 9.

Esta provavelmente não é a notícia que você queria ouvir, mas espero ter ajudado.

Olá Zach, a solução contextBridge oferece suporte a protótipos?
Meu código original com contextIsolation: false simplesmente anexou uma função em window . Esta função retorna Promise<MediaStream> porém a documentação de contextBridge diz que os protótipos foram descartados e quando usei contextBridge recebo um objeto vazio.
Existe uma maneira de oferecer suporte a protótipos, mas também garantindo contextIsolation: true ?

Não, não importa. Eu estava tentando encontrar um comentário de um dos membros da equipe de que me lembro que esse era o comportamento pretendido, mas não consegui. O melhor que posso dizer é no Electron v8, ( pr 20214 ) trouxe mudanças para a serialização de objetos dentro do IPC (efetivamente quando o isolamento de contexto está ativado) que irá descartar o suporte (atualmente obsoleto, será removido em versões futuras) para funções ou protótipos.

NOTA: Objetos que não são serializáveis ​​com o algoritmo Structured Clone do V8, como funções, objetos DOM, objetos Node / Electron especiais como process.env ou WebContents, ou quaisquer objetos que contenham tais itens serão serializados com a base antiga :: Value- algoritmo baseado. No entanto, esse comportamento está obsoleto e lançará uma exceção começando com o Electron 9.

Esta provavelmente não é a notícia que você queria ouvir, mas espero ter ajudado.

Isso é lamentável, mas obrigado por sua resposta rápida.

Eu segui as etapas descritas acima (fornecidas por @reZach e @aplum) e estou passando informações do meu client.js para o preload.js para main.js .

Ao executar tudo localmente, a comunicação funciona, mas quando executo electron-builder , a comunicação é interrompida. Por exemplo, usar setBadge() funciona maravilhosamente quando executado localmente. Eu posso pegar a contagem do arquivo client.js e vê-la sendo passada para contextBridge e então para main.js e configurando app.dock.setBadge(count) sucesso. Há algo que estou perdendo na comunicação no Electron depois que ele foi construído com 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)

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 , não sou um mantenedor da equipe de elétrons, mas essa pode ser uma pergunta mais adequada para o repositório de construção de elétrons.

Estou mais inclinado a pensar que é um problema com o código do que com o criador de elétrons quando parei um momento para pensar sobre isso.

Estou mais inclinado a pensar que é um problema com o código do que com o criador de elétrons quando parei um momento para pensar sobre isso.

Tenho certeza de que estou perdendo alguma peça-chave que faria tudo isso funcionar conforme o esperado. Obrigado pela sua resposta. Abri um problema no repo electron-builder , mas ele não recebeu qualquer tração.

Estou mais inclinado a pensar que é um problema com o código do que com o criador de elétrons quando parei um momento para pensar sobre isso.

Tenho certeza de que estou perdendo alguma peça-chave que faria tudo isso funcionar conforme o esperado. Obrigado pela sua resposta. Abri um problema no repo electron-builder , mas ele não recebeu qualquer tração.

Você criou um repositório MVP com o seu problema? Isso às vezes ajuda e, no processo, você percebe o que está acontecendo.

@Amthieu e @CiriousJoker eu amo vocês dois, obrigado.

  1. yarn add -D customize-cra react-app-rewired
  2. modifique o package.json para usar o app react-app-rewired start: "start": "BROWSER = none react-app-rewired start",
  3. crie o arquivo 'config-overrides' no diretório raiz do app react:
    `const {override} = require ('customize-cra');

function addRendererTarget (config) {
config.target = 'renderizador de elétrons';

return config;
}

module.exports = override (addRendererTarget); `

  1. você pode importar {ipcRenderer} de 'electron' agora :)

Estou usando vuejs e basta adicionar o código em vue.config.js

module.exports = {
"transpileDependencies": [
"vuetificar"
],
pluginOptions: {
electronBuilder: {
nodeIntegration: true
}
}
}

@genilsonmm Caramba, que funcionou! Estou enlouquecendo nas últimas 3 horas. Obrigado!!
A parte específica que funcionou para mim foi o
`` `javascript
pluginOptions: {
electronBuilder: {
nodeIntegration: true
}
},

"transpileDependencies": [
"vuetificar"
],

isso não está funcionando comigo ... mas window.require ('elétron') está funcionando bem

Para todas as pessoas que têm o problema "window.require is not a function"

Você deve criar um script de pré-carregamento no elétron.

  1. Crie um arquivo chamado preload.js no diretório onde você tem o script principal do electron, coloque este código nele:

    window.require = require;

  2. Vá para o script principal do elétron e digite no código em que você cria a janela:

    win = new BrowserWindow ({
    largura: 1280,
    altura: 720,
    webPreferences: {
    nodeIntegration: false,
    preload: __dirname + '/preload.js'
    },

    })
    Com isso você vai pré-carregar o script antes de tudo, isso resolveu o problema para mim. Espero também por você :)

@reZach, sua solução funcionou para mim, mas percebi um erro que outras pessoas podem tropeçar (porque eu tropecei, uau!):

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

você define sua função de retorno de chamada como "func", mas então chama "fn", se você alterar isso, funciona exatamente como você descreveu.
Muito obrigado pela postagem detalhada: +1:

@genilsonmm Se eu colocar vue.config.js:

pluginOptions: {
electronBuilder: {
nodeIntegration: true,

Recebo o erro "requer não está definido":

image

Se eu comentar a linha "nodeIntegration: true", a mensagem de erro desaparece e o aplicativo funciona.

O que isso significa?

@marcoippolito Eu acredito que significa que o código empacotado do webpack está tentando usar require que é o método de resolução de módulo do node js. Como isso não estará disponível se você desativar a integração do nó, o código não poderá ser executado. O que você precisa fazer é modificar a configuração do webpack para os navegadores de destino ( var é o destino, se bem me lembro) e garantir que nenhum de seus códigos esteja usando apis de nó e que o erro desapareça.

Para aqueles que usam TypeScript, há um bom exemplo nos exemplos oficiais do 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 Caramba, que funcionou! Estou enlouquecendo nas últimas 3 horas. Obrigado!!
A parte específica que funcionou para mim foi o

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

正 解 , 标记

Eu também tenho esse problema com o NodeIntegration habilitado. Ambos window.require e require não funcionam.

+ Isso só acontece com o reat, não o elétron simples

Esta página foi útil?
0 / 5 - 0 avaliações