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
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.
- Crie um
preload.js file
com o código:window.ipcRenderer = require('electron').ipcRenderer;
- Pré-carregue este arquivo em seu main.js via
webPreferences
:mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: false, preload: __dirname + '/preload.js' } });
- 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:
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:
"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;
};
new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
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.
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 manternodeIntegration: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 ataquenodeIntegration:true
(incrivelmente raro, mas pode acontecer)!
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.
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.
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.
@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.
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:
preload
. Esta propriedade é um arquivo js que carrega com acesso a require
(o que significa que você pode exigir ipcRenderer)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çãoipcRenderer
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.
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
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 emwindow
. Esta função retornaPromise<MediaStream>
porém a documentação decontextBridge
diz que os protótipos foram descartados e quando useicontextBridge
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 comcontextIsolation: false
simplesmente anexou uma função emwindow
. Esta função retornaPromise<MediaStream>
porém a documentação decontextBridge
diz que os protótipos foram descartados e quando useicontextBridge
recebo um objeto vazio.
Existe uma maneira de oferecer suporte a protótipos, mas também garantindocontextIsolation: 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",
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}`)
})
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld(
'setBadgeCountForElectron', {
send: (channel, data) => {
console.log(`Preload ${channel}, ${data}`)
ipcRenderer.send(channel, data)
}
}
)
const { setBadgeCountForElectron } = window;
function sendBadgeCount(count) {
!!setBadgeCountForElectron && setBadgeCountForElectron.send('SEND_BADGE_COUNT', count)
}
sendBadgeCount(count)
"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.
function addRendererTarget (config) {
config.target = 'renderizador de elétrons';
return config;
}
module.exports = override (addRendererTarget); `
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.
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;
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":
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 opluginOptions: { 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
Comentários muito úteis
@MarshallOfSound meu erro.
Eu encontrei a solução no problema nº 7300 se ela puder ajudar alguém.
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).