J'ai créé une application de réaction simple avec create-react-app et je l'ai intégrée avec succès à electron. Tout fonctionnait très bien jusqu'à ce que j'essaye d'importer des électrons dans le fichier du créateur d'action. Si je supprime la ligne ci-dessous, l'application fonctionne correctement. Le problème est que je ne peux pas utiliser ipcRenderer pour communiquer du côté réactif au processus principal des électrons.
Cette ligne fait planter l'application :
import { ipcRenderer } from 'electron';
J'obtiens l'erreur suivante :
TypeError : fs.existsSync n'est pas une fonction
(fonction anonyme)
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')
J'ai découvert sur Google qu'il s'agit d'un problème courant lorsque l'on essaie d'importer des électrons.
Merci pour l'aide
CRA utilise webpack qui perturbe le chargement de module standard (y compris fs).
Je recommanderais d'examiner le mode Electron pour le pack Web et de l'éjecter de CRA
Les problèmes de GitHub concernent les demandes de fonctionnalités et les rapports de bogues, les questions sur l'utilisation d'Electron doivent être adressées à la communauté ou à Slack Channel .
@MarshallOfSound mon erreur.
J'ai trouvé la solution dans le problème #7300 si cela peut aider quelqu'un.
const { ipcRenderer } = window.require('electron');
Veuillez noter que cela fonctionnera lorsque vous exécuterez l'application Electron, mais si vous souhaitez simplement tester votre code React dans le navigateur, il plantera toujours (window.require n'est pas défini dans le navigateur comme dans Electron).
Si vous souhaitez accéder à app.quit(), vous pouvez utiliser ceci :
const { app } = window.require('electron').remote;
Peut-être que ça aide quelqu'un...
@CiriousJoker ce sont des solutions, merci !
Je reçois toujours window.require is not a function
. J'utilise Electron avec React Starter Kit (https://github.com/kriasoft/react-starter-kit). Tout fonctionne bien, sauf ça.
J'ai configuré mon application Electron pour charger mon application à partir du Web, donc l'application ne s'exécute pas localement :
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6
Ce que j'essaie de faire, c'est d'appeler le ipcRenderer
dans l'un de mes fichiers React. Je ne sais pas si c'est même possible lorsque mon application est chargée à partir du Web. Aucune suggestion?
@holgersindbaek
Dans le même bateau que vous... Avez-vous trouvé une solution ?
Non. Je suis presque sûr qu'il n'est pas possible de charger ipcRenderer à partir du navigateur.
Si vous exécutez votre application React dans le navigateur, cela ne fonctionnera pas. Exécutez-le dans Electron et tout devrait bien se passer.
@Amthieu Merci pour le conseil. Je ne sais toujours pas comment je peux exécuter mon projet React (basé sur React Starter Kit) dans Electron. Tout avis serait grandement apprécié:
https://discuss.atom.io/t/getting-electron-to-work-with-react-starter-kit/48594
D'accord, j'ai une solution.
1) Créez un preload.js file
avec le code :
window.ipcRenderer = require('electron').ipcRenderer;
2) Préchargez ce fichier dans votre main.js via webPreferences
:
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
preload: __dirname + '/preload.js'
}
});
3) Maintenant, vous aurez accès à partir de votre application React. Par exemple, cela fonctionnera :
componentDidMount() {
if (isElectron()) {
console.log(window.ipcRenderer);
window.ipcRenderer.on('pong', (event, arg) => {
this.setState({ipc: true})
})
window.ipcRenderer.send('ping')
}
}
Remarque - en utilisant ceci : https://github.com/cheton/is-electron pour la fonction isElectron()
@HemalR L' étape 3 devrait être la suivante (maintenant) :
componentDidMount() {
if (window.isElectron) {
console.log(window.ipcRenderer);
window.ipcRenderer.on('pong', (event, arg) => {
this.setState({ipc: true})
})
window.ipcRenderer.send('ping')
}
}
Remarque : window.isElectron
n'est pas une fonction.
@nparsons08
Excuses - j'aurais dû ajouter d'où je reçois isElectron, j'ai modifié mon exemple de code avec le lien vers : https://github.com/cheton/is-electron
@holgersindbaek
Y a-t-il une solution maintenant
pour moi, ne fonctionne que si nodeIntegration
est true
;
webPreferences: {
nodeIntegration: true,
preload: __dirname + '/preload.js'
}
fonctionne très bien la solution @HemalR !
maintenant COMMENT envoyer DE L'électron À React ?
essayé avec
du côté des électrons
ipcMain.emit("pong", "Hello!");
mais rien n'a été reçu de l'auditeur React
window.ipcRenderer.on("pong", (event, arg) => {
console.log("PONG");
});
est correct d'utiliser ipcMain.emit() ou dois-je utiliser autre chose ?
ok je viens de découvrir que je dois utiliser (sur le processus principal d'électrons)
mainWindow.webContents.send("pong", "Hello!");
merci à tous !
J'ai essayé tout ce qui précède en vain. Ce qui a fonctionné pour moi, c'est un hack géant. Modifiez le fichier ./node_modules/electron/index.js
et codez en dur le chemin vers electron.exe
par exemple
function getElectronPath() {
return 'D:\\Repos\\MyProject\\node_modules\\electron\\dist\\electron.exe';
}
module.exports = getElectronPath();
Wow, je n'ai pas pu faire fonctionner IPCRenderer sur mes composants React. J'ai essayé toutes les méthodes ci-dessus. L'un d'entre vous a-t-il d'ailleurs eu des indices que je peux utiliser pour que cela fonctionne ? Merci
Hmmm... Mon application électronique fonctionne toujours très bien en utilisant ma solution ci-dessus - mais je ne l'ai pas mise à jour depuis quelques mois maintenant (je n'en ai pas eu besoin).
Je me demande s'il y a un changement de rupture qui empêcherait cela de fonctionner? Peut-être que vous pouvez poster vos versions électroniques ?
@cyclonstep Y a-t-il une erreur spécifique que vous
J'utilise le colis pour le groupage.
Window.require l'a également résolu pour moi (montrant également, ce qui n'a pas été le cas):
importer Vue depuis 'vue/dist/vue.min'
importer l'application depuis './App'// MAL? importer { ipcRenderer } depuis 'electron'
// MAL? const { ipcRenderer } = require('électron')
// BON:
const { ipcRenderer } = window.require('electron')
( plus bas dans le même fichier se trouve l'électron "pong-Demo" , ce qui prouve un peu que ça marche)
Peut-être à noter : même lorsque vous vous trompez, la taille du faisceau n'augmente pas (comparée à l'absence d'électrons requis. C'est jusqu'à présent ma première importation d'électrons côté rendu) de la taille entière de l'électron ou autre, mais seulement d'environ 20 ko , qui semble être un code shim/wrapper à lui seul, provenant 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
Quoi qu'il en soit, les choses fonctionnent comme dit ci-dessus.
Noeud version 10.2.0
Chrome version 66.0.3359.181
Version électronique 3.0.2
window.require
ne fonctionnait pas pour moi dans mon script principal avec l'erreur window is not defined
, alors je suis passé à const electron = eval('require')("electron")
. J'espère que cela aide quelqu'un. J'utilisais webpack, et le problème était que webpack évaluait mon instruction require au moment de la compilation.
@MarshallOfSound mon erreur.
J'ai trouvé la solution dans le problème #7300 si cela peut aider quelqu'un.
const { ipcRenderer } = window.require('electron');
Veuillez noter que cela fonctionnera lorsque vous exécuterez l'application Electron, mais si vous souhaitez simplement tester votre code React dans le navigateur, il plantera toujours (window.require n'est pas défini dans le navigateur comme dans Electron).
Et pour le tapuscrit :
import {IpcRenderer} from 'electron';
declare global {
interface Window {
require: (module: 'electron') => {
ipcRenderer: IpcRenderer
};
}
}
const { ipcRenderer } = window.require('electron');
@moshfeu Votre solution fonctionne à
Pour taper et utiliser l'exemple de @HemalR ci-dessus mais SANS nodeIntegration: true
: https://github.com/electron/electron/issues/9920#issuecomment -336757899 :
D'accord, j'ai une solution.
- Créez un
preload.js file
avec le code :window.ipcRenderer = require('electron').ipcRenderer;
- Préchargez ce fichier dans votre main.js via
webPreferences
:mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: false, preload: __dirname + '/preload.js' } });
- Maintenant, vous aurez accès à partir de votre application de réaction. Par exemple, cela fonctionnera :
componentDidMount() { if (isElectron()) { console.log(window.ipcRenderer); window.ipcRenderer.on('pong', (event, arg) => { this.setState({ipc: true}) }) window.ipcRenderer.send('ping') } }
Remarque - en utilisant ceci : https://github.com/cheton/is-electron pour la fonction
isElectron()
combiné avec
https://github.com/electron/electron/issues/9920#issuecomment -447157348
J'ai utilisé ceci :
import { IpcRenderer } from 'electron';
declare global {
interface Window {
ipcRenderer: IpcRenderer
}
}
export const { ipcRenderer } = window;
J'espère que cela aidera quelqu'un là-bas ! Fonctionne avec des stenciljs, et j'imagine réagir et angulaire
ajoutez simplement la cible : "electron-renderer" dans les configurations de webpack.
exporter par défaut {
...
cible : « restituteur d'électrons »
...
}
Bonjour, j'utilise CRA + Electron. Et je construis mon projet par cible 'electron-renderer'. Cela fonctionne bien lors du chargement de fichiers dans le dossier de construction, mais il génère une erreur lorsque je développe et charge l'url localhost:3000
. Je suppose que la raison en est que j'utilise node api et electron api dans les composants de réaction. Quelqu'un a la solution ? Merci.
TypeError: fs.existsSync is not a function
Oui, l'api fs
n'est pas disponible dans le navigateur. Si je comprends bien la situation, vous exécutez votre application en utilisant react-scripts start
(qui est le script par défaut de npm start
dans CRA).
La bonne façon de procéder est d'exécuter electron .
. Vous pouvez le voir dans la doc : https://electronjs.org/docs/tutorial/first-app.
LMK si cela fonctionne pour vous. Vous pouvez également voir comment cela fonctionne dans mon application - https://github.com/moshfeu/y2mp3 (Notez qu'il n'a pas été créé avec CRA)
Bonjour, j'utilise CRA + Electron. Et je construis mon projet par cible 'electron-renderer'. Cela fonctionne bien lors du chargement de fichiers dans le dossier de construction, mais il génère une erreur lorsque je développe et charge l'url
localhost:3000
. Je suppose que la raison en est que j'utilise node api et electron api dans les composants de réaction. Quelqu'un a la solution ? Merci.
TypeError: fs.existsSync is not a function
ajoutez simplement la cible : "electron-renderer" dans les configurations de webpack.
exporter par défaut {
...
cible : « restituteur d'électrons »
...
}
Ça marche pour moi aussi.
Voici ma version d'outils :
Je pense que electron-renderer
juste pour résoudre ce problème d'électrons. Je n'ai pas besoin d'utiliser une syntaxe filaire comme window.required
, et il a même perdu la frappe !
Pour moi, j'ai travaillé sur la définition de la cible : "rendu d'électrons" pour le pack Web et l'intégration de nœud : vrai dans les options de BrowserWindow
J'utilise webpack+electron+react des dernières versions d'aujourd'hui
J'exécute create-react-app avec TypeScript et Electron. J'ai suivi ces très bonnes instructions pour une configuration. Mais j'ai aussi rencontré cette erreur. La solution qui fonctionne pour moi est la somme des choses dites dans ce fil:
"electron-renderer"
comme target
. J'utilise rescripts
avec rescript-env
pour cela.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;
}
Et toi enfin dans ton appli
App.tsx
const { remote } = window.require("electron");
console.log(remote.getCurrentWindow());
@LukasBombach
Cela devrait fonctionner aussi :
declare var window: Window;
interface Window {
require: NodeRequire
}
Ensuite, vous aurez à taper sur les consts requis
Merci!
Voici les étapes que j'ai utilisées pour Vue au cas où cela serait utile à quelqu'un d'autre.
Assurez-vous que l'intégration des nœuds est activée pour le processus de rendu en l'ajoutant aux préférences Web lors de la création d'une fenêtre de navigateur :
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',
},
}
Importez ce dont vous avez besoin dans votre application. J'importe le module shell dans mon composant comme ceci :
import { shell } from 'electron'
A tous ceux qui ont toujours le même problème. C'est la meilleure solution que j'ai trouvé jusqu'à présent
```js
nouvelle fenêtre de navigateur({
Préférences Web : {
nodeIntegration : vrai
}
});
J'espère que ce commentaire sera remarqué, car beaucoup de gens demandent à importer fs
ou ipcRenderer
dans vos applications. C'est un besoin commun pour les applications électroniques, mais j'ai trouvé que peu de gens avaient raison et utilisaient des modèles obsolètes. tl;dr - il y a une faille de sécurité si vous n'importez pas votre module de nœud (c'est-à-dire fs
) ou votre module électronique (c'est-à-dire ipcRenderer
) de la bonne manière. Si vous utilisez votre application uniquement pour vous-même, vous êtes _probablement_ en sécurité, mais si jamais vous souhaitez partager ou vendre votre application, vous devriez lire à l'avance.
Avant d'entrer dans la solution, il est important de comprendre _pourquoi_ nous faisons cela en premier lieu. Les applications Electron nous permettent d'inclure des modules de nœuds dans nos applications, ce qui leur confère une puissance incroyable, mais des problèmes de sécurité. Nous voulons permettre à notre application d'utiliser les fonctionnalités natives du système d'exploitation (c'est-à-dire le nœud), mais nous ne voulons pas qu'elles soient utilisées de
Comme évoqué par
nodeIntegration:true
. Cependant, je choisirais toujours de gardernodeIntegration:false
pour protéger les utilisateurs accidentels/malveillants utilisant votre application, et empêcher tout malware éventuel qui pourrait être installé sur votre machine d'interagir avec votre application électronique et d'utiliser le vecteur d'attaquenodeIntegration:true
(incroyablement rare, mais pourrait arriver) !
La définition de nodeIntegration: true
dans votre BrowserWindow donne à votre processus de rendu l'accès aux modules de nœuds. Faire _ce_, est vulnérable. Vous avez accès à require("fs")
et require("electron")
, mais cela signifie que si quelqu'un découvrait une vulnérabilité XSS, il pourrait exécuter n'importe quelle commande que vous avez exposée dans votre processus de rendu.
Pensez à supprimer tous les fichiers sur votre ordinateur, ou quelque chose d'autre qui est vraiment mauvais.
En plus de définir nodeIntegration sur true, il est probable que votre application utilise webpack pour regrouper les fichiers d'application. Webpack perturbe certains symboles, donc des paramètres comme target: 'electron-renderer'
ou les externes webpack vous permettent de transmettre ces variables ( ipcRenderer
) à votre application à la place.
Pourtant, cela ne change rien, sauf la façon dont vous configurez votre application.
Vous pouvez utiliser le module distant qui vous donne accès à ipcRenderer
. Il s'agit essentiellement de « la méthode facile » sous une forme différente. Cela n'est pas recommandé par les recommandations de sécurité d'Electron car ce type d'attaque souffre d'un prototype de vecteur de pollution.
C'est à dire. l'utilisation de la télécommande pourrait permettre à quelqu'un de modifier le prototype d'un objet js et de faire des ravages sur votre machine/application.
@marksyzm a une meilleure solution , bien que pas parfaite, où nous utilisons IPC pour envoyer le ipcRenderer
au processus de rendu. Ce type de configuration est également vulnérable aux prototypes d' attaques de
La bonne façon d'importer vos fs
/ ipcRenderer
dans votre processus de rendu est d'utiliser IPC (communication inter-processus). C'est la manière d'Electron de vous permettre de discuter entre le processus principal et le processus de rendu. Voici à quoi doit ressembler votre application :
preload
. Cette propriété est un fichier js qui se charge avec un accès à require
(ce qui signifie que vous pouvez exiger ipcRenderer)contextIsolation: true
pour empêcher les prototypes d'attaques de pollution, mais cela signifie que vous devez utiliser le contextBridge pour transmettre l'ipcRenderer à votre processus de rendu.ipcRenderer
fs
_En gros_ voici à quoi ressemblent toutes ces étapes :
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>
À tout le moins, je crois que vous avez besoin d'électrons v7 pour ces fonctionnalités.
Je me soucie des applications électroniques sécurisées et j'ai construit secure-electron-template
afin de créer un modèle d'application électronique pour intégrer la sécurité au lieu de considérer la sécurité comme une réflexion après coup.
Sur la base du commentaire de preload.js , où mon API est plus proche de celle d'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();
}
Et pour ceux qui utilisent 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;
}
Est-ce que quelqu'un sait pourquoi j'obtiens une erreur de module non trouvé lorsque j'essaie de le mettre dans preload.js ?
const errorLogging = require('../renderer/utils/errorLogging');
Merci
@silentlight ce n'est pas lié à l'électron, avez-vous peut-être votre propre require()
n'est pas correct et c'est pourquoi il renvoie une erreur, sans voir plus de code).
Bien qu'il existe déjà la solution window.require
, je vais partager la mienne, juste pour le fun :
function magic(module){
require(module)
}
const { ipcRenderer } = magic('electron')
Vos bundlers ne s'identifieront pas lorsque vous importez un module car vous n'utilisez pas de modules ES ou de syntaxe CommonJS, il n'essaiera donc pas de le regrouper et ne générera donc pas d'erreur
Je me demande pourquoi personne n'a encore essayé ça 😆
@marc2332 true, mais vous vous exposez alors directement à des attaques potentielles de ce wrapper magique. Cela peut ne pas s'appliquer à votre scénario, mais c'est certainement une préoccupation que vous devriez considérer.
@reZach Je ne vois aucune raison pour laquelle le window.require est plus sécurisé pour être honnête. Au fait, il y a des façons plus sales de le faire.
const { ipcRenderer } = eval("require('electron')")
@ marc2332 ni l'
Merci reZach ! Je pense que les gens comprendront votre approche plus tôt
Mark Elphinstone
www.oxfordsourceltd.com
Salut Zach, la solution contextBridge
charge les prototypes ?
Mon code d'origine avec contextIsolation: false
simplement attaché une fonction sur window
. Cette fonction renvoie un Promise<MediaStream>
mais la documentation de contextBridge
indique que les prototypes sont supprimés et que lorsque j'ai utilisé le contextBridge
je reçois un objet vide.
Existe-t-il un moyen de prendre en charge les prototypes mais également de garantir contextIsolation: true
?
Salut Zach, la solution
contextBridge
charge les prototypes ?Mon code d'origine avec
contextIsolation: false
simplement attaché une fonction surwindow
. Cette fonction renvoie unPromise<MediaStream>
mais la documentation decontextBridge
indique que les prototypes sont supprimés et que lorsque j'ai utilisé lecontextBridge
je reçois un objet vide.Existe-t-il un moyen de prendre en charge les prototypes mais également de garantir
contextIsolation: true
?
Non, ce n'est pas le cas. J'essayais de trouver un commentaire dont je me souviens par l'un des membres de l'équipe selon lequel il s'agit d'un comportement intentionnel, mais je n'ai pas pu. Le mieux que je puisse dire est dans Electron v8, ( pr 20214 ) a apporté des modifications à la sérialisation des objets dans IPC (effectivement lorsque l'isolation du contexte est
REMARQUE : Les objets qui ne sont pas sérialisables avec l'algorithme de clonage structuré de V8, tels que les fonctions, les objets DOM, les objets Node/Electron spéciaux comme process.env ou WebContents, ou tout objet contenant de tels éléments seront sérialisés avec l'ancienne base : : Value- algorithme basé. Cependant, ce comportement est obsolète et lèvera une exception à partir de Electron 9.
Ce n'est probablement pas la nouvelle que vous vouliez entendre, mais j'espère avoir aidé.
Salut Zach, la solution
contextBridge
charge les prototypes ?
Mon code d'origine aveccontextIsolation: false
simplement attaché une fonction surwindow
. Cette fonction renvoie unPromise<MediaStream>
mais la documentation decontextBridge
indique que les prototypes sont supprimés et que lorsque j'ai utilisé lecontextBridge
je reçois un objet vide.
Existe-t-il un moyen de prendre en charge les prototypes mais également de garantircontextIsolation: true
?Non, ce n'est pas le cas. J'essayais de trouver un commentaire dont je me souviens par l'un des membres de l'équipe selon lequel il s'agit d'un comportement intentionnel, mais je n'ai pas pu. Le mieux que je puisse dire est dans Electron v8, ( pr 20214 ) a apporté des modifications à la sérialisation des objets dans IPC (effectivement lorsque l'isolation du contexte est
REMARQUE : Les objets qui ne sont pas sérialisables avec l'algorithme de clonage structuré de V8, tels que les fonctions, les objets DOM, les objets Node/Electron spéciaux comme process.env ou WebContents, ou tout objet contenant de tels éléments seront sérialisés avec l'ancienne base : : Value- algorithme basé. Cependant, ce comportement est obsolète et lèvera une exception à partir de Electron 9.
Ce n'est probablement pas la nouvelle que vous vouliez entendre, mais j'espère avoir aidé.
C'est dommage, mais merci pour ta réponse rapide.
J'ai suivi les étapes décrites ci-dessus (données par @reZach et @aplum), et je transmets des informations de mon client.js
au preload.js
à main.js
.
Lorsque j'exécute tout localement, la communication fonctionne, mais lorsque j'exécute electron-builder
, la communication est interrompue. Par exemple, l'utilisation de setBadge()
fonctionne à merveille lorsqu'elle est exécutée localement. Je peux prendre le compte du fichier client.js
et le voir être passé dans le contextBridge
puis dans main.js
et définir avec succès le app.dock.setBadge(count)
. Y a-t-il quelque chose qui me manque avec la communication dans Electron après sa construction avec 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 , je ne suis pas un responsable de l'équipe d'électrons, mais cela pourrait être une meilleure question adaptée au référentiel de constructeurs d'électrons.
Je suis plus enclin à penser qu'il s'agit d'un problème de code plutôt que de générateur d'électrons lorsque j'ai pris un moment pour y réfléchir.
Je suis plus enclin à penser qu'il s'agit d'un problème de code plutôt que de générateur d'électrons lorsque j'ai pris un moment pour y réfléchir.
Je suis sûr qu'il me manque un élément clé qui permettrait à tout cela de fonctionner comme prévu. Merci pour votre réponse. J'ai ouvert un problème dans le electron-builder
, mais il n'a reçu aucune traction.
Je suis plus enclin à penser qu'il s'agit d'un problème de code plutôt que de générateur d'électrons lorsque j'ai pris un moment pour y réfléchir.
Je suis sûr qu'il me manque un élément clé qui permettrait à tout cela de fonctionner comme prévu. Merci pour votre réponse. J'ai ouvert un problème dans le
electron-builder
, mais il n'a reçu aucune traction.
Avez-vous créé un dépôt MVP avec votre problème ? Cela aide parfois et dans le processus, vous réalisez ce qui se passe.
@Amthieu et @CiriousJoker je vous aime tous les deux merci.
function addRendererTarget(config) {
config.target = 'electron-renderer';
retourner la configuration ;
}
module.exports = override(addRendererTarget);`
J'utilise vuejs et ajoute simplement le code dans vue.config.js
module.exports = {
"transpileDependencies": [
"vuetifier"
],
pluginOptions : {
electronBuilder : {
nodeIntegration : vrai
}
}
}
@genilsonmm Putain de merde qui a fonctionné ! Je deviens fou depuis 3 heures. Merci!!
La partie spécifique de cela qui a fonctionné pour moi était le
```javascript
pluginOptions : {
electronBuilder : {
nodeIntegration : vrai
}
},
"transpileDependencies": [
"vuetifier"
],
cela ne fonctionne pas sur moi... mais window.require('electron') fonctionne bien
Pour toutes les personnes qui ont le problème "window.require n'est pas une fonction"
Vous devez créer un script de préchargement sur electron.
Créez un fichier nommé preload.js dans le répertoire où vous avez le script principal d'électron, placez ce code dessus :
window.require = exiger;
Accédez à votre script principal d'électron et tapez ceci dans le code où vous créez la fenêtre :
win = new BrowserWindow({
largeur : 1280,
hauteur: 720,
Préférences Web : {
nodeIntegration : faux,
précharger : __dirname + '/preload.js'
},
})
Avec cela, vous allez précharger le script avant tout, cela a résolu le problème pour moi. j'espère aussi pour toi :)
@reZach, votre solution a fonctionné pour moi, mais j'ai remarqué une erreur sur laquelle d'autres personnes pourraient trébucher (parce que je l'ai fait, oups !):
receive: (channel, func) => { let validChannels = ["fromMain"]; if (validChannels.includes(channel)) { // Deliberately strip event as it includes `sender` ipcRenderer.on(channel, (event, ...args) => fn(...args)); } }
vous définissez votre fonction de rappel comme "func" , mais appelez ensuite "fn", si vous modifiez cela, cela fonctionne exactement comme vous le décrivez.
Un grand merci pour le post détaillé :+1:
@genilsonmm Si je mets vue.config.js :
pluginOptions : {
electronBuilder : {
nodeIntegration : vrai,
J'obtiens l'erreur "require is notdefined":
Si je commente la ligne "nodeIntegration: true", le message d'erreur disparaît et l'application fonctionne.
Qu'est-ce que ça veut dire?
@marcoippolito Je pense que cela signifie que le code fourni avec le pack Web essaie d'utiliser require
qui est la méthode de résolution de module de node js. Comme cela n'est pas disponible si vous désactivez l'intégration de nœud, le code ne peut pas s'exécuter. Ce que vous devez faire est de modifier votre configuration webpack pour cibler les navigateurs ( var
est la cible si je me souviens bien) et assurez-vous qu'aucun de votre code n'utilise node apis et l'erreur devrait disparaître.
Pour ceux qui utilisent TypeScript, il y a un bon exemple dans les exemples officiels de 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 Putain de merde qui a fonctionné ! Je deviens fou depuis 3 heures. Merci!!
La partie spécifique de cela qui a fonctionné pour moi était lepluginOptions: { electronBuilder: { nodeIntegration: true } },
??
J'ai aussi ce problème avec NodeIntegration activé. Les deux window.require et require ne fonctionnent pas.
+ Cela ne se produit qu'avec des électrons réactifs, pas simples
Commentaire le plus utile
@MarshallOfSound mon erreur.
J'ai trouvé la solution dans le problème #7300 si cela peut aider quelqu'un.
Veuillez noter que cela fonctionnera lorsque vous exécuterez l'application Electron, mais si vous souhaitez simplement tester votre code React dans le navigateur, il plantera toujours (window.require n'est pas défini dans le navigateur comme dans Electron).