Electron: Ошибка при импорте электрона в React | импортировать {ipcRenderer} из "электрона"

Созданный на 3 июл. 2017  ·  65Комментарии  ·  Источник: electron/electron

Я создал простое приложение для реагирования с приложением create-react-app и успешно интегрировал его с электроном. Все работало отлично, пока я не попытался импортировать электрон в файл создателя действий. Если я удалю строку ниже, приложение будет работать нормально. Проблема в том, что я не могу использовать ipcRenderer для связи со стороны реагирования с основным процессом электрона.

Эта строка вызывает сбой приложения:
import { ipcRenderer } from 'electron';

Я получаю следующую ошибку:

TypeError: fs.existsSync не является функцией
(анонимная функция)
node_modules / электрон / 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')

Я узнал в Google, что это обычная проблема при попытке импортировать электрон.

Спасибо за помощь

Самый полезный комментарий

@MarshallOfSound мою ошибку.

Я нашел решение проблемы № 7300, если оно может кому-нибудь помочь.

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

Обратите внимание, что это будет работать, когда вы запустите приложение Electron, но если вы просто хотите протестировать свой код React внутри браузера, он все равно выйдет из строя (window.require не определен в браузере, как в Electron).

Все 65 Комментарий

CRA использует webpack, который мешает загрузке стандартных модулей (включая fs).

Я бы порекомендовал заглянуть в электронный режим для webpack и извлечь из CRA

Проблемы с GitHub связаны с запросами функций и отчетами об ошибках, вопросы об использовании Electron следует направлять сообществу или в Slack Channel .

@MarshallOfSound мою ошибку.

Я нашел решение проблемы № 7300, если оно может кому-нибудь помочь.

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

Обратите внимание, что это будет работать, когда вы запустите приложение Electron, но если вы просто хотите протестировать свой код React внутри браузера, он все равно выйдет из строя (window.require не определен в браузере, как в Electron).

Если вы хотите получить доступ к app.quit (), вы можете использовать это:

const {приложение} = window.require ('электрон'). удаленный;

Может кому поможет ...

@CiriousJoker, это решения, спасибо!

Я все еще получаю window.require is not a function . Я использую Electron с React Starter Kit (https://github.com/kriasoft/react-starter-kit). Все работает нормально, кроме этого.

Я настроил приложение Electron для загрузки моего приложения из Интернета, поэтому приложение не работает локально:
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6

Я пытаюсь вызвать ipcRenderer в одном из моих файлов React. Я не уверен, возможно ли это даже, когда мое приложение загружается из Интернета. Какие-либо предложения?

@holgersindbaek

В той же лодке, что и вы ... Вы нашли решение?

Нет. Я почти уверен, что ipcRenderer невозможно загрузить из браузера.

Если вы запускаете приложение React в браузере, оно не будет работать. Запустите его в Electron, и все будет в порядке.

@Amthieu Спасибо за совет. Я все еще сомневаюсь, как я могу запустить свой проект React (на основе React Starter Kit) в Electron. Мы будем благодарны за любые советы:

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

Хорошо, у меня есть решение.

1) Создайте preload.js file с кодом:

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

2) Предварительно загрузите этот файл в свой main.js через webPreferences :

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

3) Теперь у вас будет доступ из вашего приложения для реагирования. Например, это будет работать:

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

Примечание - используя это: https://github.com/cheton/is-electron для функции isElectron()

@HemalR Шаг 3 должен быть следующим (сейчас):

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

Примечание . window.isElectron не является функцией.

@ nparsons08

Извинения - должен был добавить, откуда я получаю isElectron, отредактировал мой пример кода со ссылкой на: https://github.com/cheton/is-electron

@holgersindbaek
Есть ли решение сейчас

для меня работает только в том случае, если nodeIntegration равно true ;

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

отлично работает решение

КАК ОТПРАВИТЬ С ЭЛЕКТРОНА НА React?

попробовал с
на стороне электронов

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

но от слушателя React ничего не получено

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

правильно использовать ipcMain.emit () или я должен использовать что-то еще?

хорошо, только что нашел, что я должен использовать (в основном процессе электронов)

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

Спасибо тебе за все!

Все вышеперечисленное я пробовал безрезультатно. Для меня сработал гигантский хакер. Измените файл ./node_modules/electron/index.js и жестко закодируйте путь к electron.exe

например

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

module.exports = getElectronPath();

Вау, мне не удалось заставить IPCRenderer работать с моими компонентами React. Я испробовал все вышеперечисленные методы. У кого-нибудь из вас были какие-нибудь подсказки, которыми я могу воспользоваться, чтобы он работал? Благодарность

Хммм ... Мое электронное приложение по-прежнему отлично работает с моим решением, приведенным выше, но я не обновлял его уже несколько месяцев (в этом не было необходимости).

Интересно, есть ли критическое изменение, которое остановило бы эту работу? Ребят, может, выложите свои электронные версии?

@cyclonstep Вы получали какую-то конкретную ошибку? Трудно помочь без фрагмента кода или каких-то журналов ...

Я использую посылку для комплектации.
Window.require также решил это для меня (также показывая, что не сделал):

импортировать Vue из 'vue / dist / vue.min'
импортировать приложение из './App'

// ПЛОХОЙ? импортировать {ipcRenderer} из "электрона"
// ПЛОХОЙ? const {ipcRenderer} = require ('электрон')
// ХОРОШО:
const {ipcRenderer} = window.require ('электрон')

(ниже в том же файле находится электронная «pong-Demo» , которая вроде как доказывает, что она работает)

Возможно, примечательно: даже если вы делаете это неправильно, размер пакета не увеличивается (по сравнению с без электронного запроса. Это пока мой первый и пока только импорт электронов на стороне рендеринга) на весь размер Electron или что-то в этом роде, но только примерно на 20 КБ , который, кажется, сам по себе является неким кодом прокладки / оболочки, исходящим из 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

Во всяком случае, все работает, как сказано выше.

Версия узла 10.2.0
Версия Chrome 66.0.3359.181
Электрон версия 3.0.2

window.require не работал у меня в моем основном скрипте с ошибкой window is not defined , поэтому я переключился на const electron = eval('require')("electron") . Надеюсь, это кому-то поможет. Используя webpack, проблема заключалась в том, что webpack оценивал мою инструкцию require во время компиляции.

@MarshallOfSound мою ошибку.

Я нашел решение проблемы № 7300, если оно может кому-нибудь помочь.

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

Обратите внимание, что это будет работать, когда вы запустите приложение Electron, но если вы просто хотите протестировать свой код React внутри браузера, он все равно выйдет из строя (window.require не определен в браузере, как в Electron).

А для машинописного текста:

import {IpcRenderer} from 'electron';

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

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

@moshfeu Ваше решение

Для машинописного текста и использования nodeIntegration: true : https://github.com/electron/electron/issues/9920#issuecomment -336757899:

Хорошо, у меня есть решение.

  1. Создайте preload.js file с кодом:
window.ipcRenderer = require('electron').ipcRenderer;
  1. Предварительно загрузите этот файл в свой main.js через webPreferences :
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });
  1. Теперь у вас будет доступ из вашего приложения для реагирования. Например, это будет работать:
componentDidMount() {
      if (isElectron()) {
          console.log(window.ipcRenderer);
          window.ipcRenderer.on('pong', (event, arg) => {
              this.setState({ipc: true})
          })
          window.ipcRenderer.send('ping')
      }
  }

Примечание - используя это: https://github.com/cheton/is-electron для функции isElectron()

в сочетании с
https://github.com/electron/electron/issues/9920#issuecomment -447157348

Я использовал это:

import { IpcRenderer } from 'electron';

declare global {
  interface Window {
    ipcRenderer: IpcRenderer
  }
}

export const { ipcRenderer } = window;

Надеюсь, это поможет кому-то там! Работает с stenciljs, и я представляю себе реакцию и угловатость

просто добавьте target: "electronics-renderer" в конфиги webpack.
экспорт по умолчанию {
...
цель: "электронный рендерер"
...
}

Привет, я использую CRA + Electron. И я строю свой проект с помощью целевого «электронного рендерера». Он хорошо работает при загрузке файлов в папку сборки, но выдает ошибку, когда я разрабатываю и загружаю url localhost:3000 . Думаю, причина в том, что я использую node api и electronic api в компонентах реакции. У кого-нибудь есть решение? Спасибо.

TypeError: fs.existsSync is not a function

Ага, API fs недоступен в браузере. Если я правильно понимаю ситуацию, вы запускаете свое приложение, используя react-scripts start (который является сценарием по умолчанию для npm start в CRA).

Правильный способ сделать это - запустить electron . . Вы можете увидеть это в документации: https://electronjs.org/docs/tutorial/first-app.

LMK, если это сработает для вас. Вы также можете увидеть, как это работает в моем приложении - https://github.com/moshfeu/y2mp3 (обратите внимание, что это не было создано с помощью CRA)

Привет, я использую CRA + Electron. И я строю свой проект с помощью целевого «электронного рендерера». Он хорошо работает при загрузке файлов в папку сборки, но выдает ошибку, когда я разрабатываю и загружаю url localhost:3000 . Думаю, причина в том, что я использую node api и electronic api в компонентах реакции. У кого-нибудь есть решение? Спасибо.

TypeError: fs.existsSync is not a function

просто добавьте target: "electronics-renderer" в конфиги webpack.
экспорт по умолчанию {
...
цель: "электронный рендерер"
...
}

У меня тоже работает.

Вот моя версия инструментов:

  • webpack: «3.8.1»
  • электрон: «4.2.1»

Я думаю, electron-renderer просто для решения этой электронной проблемы. Мне не нужно использовать проводной синтаксис, такой как window.required , и он даже терял типизацию!

У меня сработала установка цели: "электронный рендеринг" для webpack и nodeIntegration: true в параметрах BrowserWindow.
Я использую webpack + electronics + реакцию последних сегодняшних версий

Я запускаю приложение create-response-app с TypeScript an Electron. Я выполнил эти очень хорошие инструкции по настройке. Но потом я тоже столкнулся с этой ошибкой. Решение, которое мне подходит, - это сумма всего сказанного в этой ветке:

  1. Сделайте так, чтобы сценарий реакции использовал "electron-renderer" как target . Для этого я использую rescripts с rescript-env .

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. Добавить узел
new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
});
  1. Добавить src/typings.d.ts :
declare var window: Window;
interface Window {
  require: any;
}

И наконец ты в своем приложении

App.tsx

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

@LukasBombach
Это тоже должно сработать:

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

Тогда вам нужно будет вводить требуемые константы

Спасибо!

Вот шаги, которые я использовал для Vue на тот случай, если это поможет кому-то другому.

Убедитесь, что для процесса рендеринга включена интеграция узлов, добавив ее в веб-настройки при создании окна браузера:

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

Импортируйте то, что вам нужно, в свое приложение. Я импортирую модуль оболочки в свой компонент следующим образом:

import { shell } from 'electron'

Всем, у кого все еще есть такая же проблема. Это лучшее решение, которое я нашел на данный момент

`` js
новый BrowserWindow ({
webPreferences: {
nodeIntegration: true
}
});

Я надеюсь, что этот комментарий будет замечен, потому что многие люди спрашивают об импорте fs или ipcRenderer в свои приложения. Это обычная потребность в электронных приложениях, но я обнаружил, что не многие люди понимают это правильно и используют устаревшие шаблоны. tl; dr - существует уязвимость системы безопасности, если вы неправильно импортируете модуль узла (например, fs ) или электронный модуль (например, ipcRenderer ). Если вы используете свое приложение только для себя, вы _ вероятно_ в безопасности, но если вы когда-нибудь захотите поделиться или продать свое приложение, вам следует читать дальше.

Наша цель

Прежде чем я перейду к решению, важно понять, почему мы это делаем. Приложения Electron позволяют нам включать модули узлов в наши приложения, что дает им потрясающую мощность, но при этом заботится о безопасности. Мы хотим разрешить нашему приложению использовать функции native-os (т.е. node), но мы не хотим, чтобы ими злоупотребляли .

Как отметил @raddevus в комментарии, это необходимо при загрузке удаленного контента. Если ваше электронное приложение полностью автономно / локально, то вы, вероятно, можете просто включить nodeIntegration:true . Однако я бы все же предпочел оставить nodeIntegration:false чтобы действовать в качестве защиты от случайных / злонамеренных пользователей, использующих ваше приложение, и предотвратить взаимодействие любых возможных вредоносных программ, которые могут когда-либо быть установлены на вашем компьютере, с вашим электронным приложением и использованием вектор атаки nodeIntegration:true (невероятно редко, но может случиться)!

Легкий путь

Установка nodeIntegration: true в вашем BrowserWindow дает вашему процессу рендеринга доступ к модулям узлов. Делая это, уязвимо. У вас есть доступ к require("fs") и require("electron") , но это означает, что, если кто-то обнаружит уязвимость XSS, он сможет выполнить любую команду, которую вы указали в процессе рендеринга.

Подумайте об удалении всех файлов на вашем компьютере или о чем-то еще, что действительно плохо.

(Альтернативный) простой способ

Помимо установки для nodeIntegration значения true, вполне вероятно, что ваше приложение использует веб-пакет для объединения файлов приложения. Webpack ошибается с определенными символами, поэтому такие настройки, как target: 'electron-renderer' или внешние параметры webpack, позволяют вместо этого передавать эти переменные ( ipcRenderer ) в ваше приложение.

Тем не менее, это ничего не меняет, кроме того, как вы настраиваете свое приложение.

(Альтернативный) простой способ

Вы можете использовать удаленный модуль, который дает вам доступ к ipcRenderer . По сути, это «легкий путь» в другой форме. Рекомендации по безопасности Electron не рекомендуют этого делать, поскольку этот тип атаки страдает от прототипа вектора загрязнения.

Т.е. использование удаленного может позволить кому-то изменить прототип js-объекта и нанести ущерб вашей машине / приложению.

Практически правильный путь

@marksyzm имеет лучшее решение , хотя и не идеальное, где мы используем IPC для отправки ipcRenderer процессу рендеринга. Этот тип установки также уязвим для атак прототипа загрязнения . Если вы хотите довести свое приложение до 80%, я бы использовал этот метод, поскольку он, вероятно, не потребует от вас большого рефакторинга.

Правильный путь

Правильный способ импорта вашего fs / ipcRenderer в процесс рендеринга - это IPC (межпроцессное взаимодействие). Это способ Electron, позволяющий вам общаться между основным процессом и процессом рендеринга. Вот как должно выглядеть ваше приложение в разобранном виде:

  1. BrowserWindow имеет свойство preload . Это свойство представляет собой файл js, который загружается с доступом к require (что означает, что вам может потребоваться ipcRenderer)
  2. В вашем BrowserWindow также будет contextIsolation: true для предотвращения атак загрязнения прототипа, но это означает, что вам нужно использовать contextBridge для передачи ipcRenderer вашему процессу рендеринга.
  3. Используя сценарий предварительной загрузки и contextBridge, вы разрешаете процессу рендеринга доступ к ipcRenderer
  4. В вашем основном скрипте вы создаете слушателей для ipcRenderer (в модуле ipcMain ). В этих слушателях вы можете использовать модуль fs

_Приблизительно_ вот как выглядят все эти шаги:

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>

По крайней мере, я _ верю_, что вам нужен электрон v7 для этих функций.

Откуда мне это знать?

Я забочусь о безопасных электронных приложениях и создал secure-electron-template , чтобы создать шаблон электронного приложения для обеспечения безопасности, вместо того, чтобы думать о безопасности как о второстепенном.

Основываясь на комментарии @reZach выше, я делаю что-то вроде следующего. Основное отличие заключается в preload.js , где мой API ближе к API 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();
}

А для тех, кто использует 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;
}

Кто-нибудь знает, почему я получаю сообщение об ошибке «Модуль не найден», когда пытаюсь поместить это в preload.js?

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

Спасибо

@silentlight это не связано с электроном, возможно, у вас есть собственный репозиторий на github, где можно продолжить разговор? (Похоже, ваш путь в require() неверен, и поэтому он выдает ошибку, не видя больше кода).

Хотя решение window.require , я поделюсь своим, просто для удовольствия:

function magic(module){
    require(module)
}

const { ipcRenderer } = magic('electron')

Ваши сборщики не будут идентифицировать, поскольку вы импортируете модуль, поскольку вы не используете модули ES или синтаксис CommonJS, поэтому он не будет пытаться связать его и поэтому не выдаст ошибку

Интересно, почему это еще никто не пробовал 😆

@ marc2332 true, но тогда вы напрямую подвергаетесь потенциальным атакам этой волшебной обертки. Это может не относиться к вашему сценарию, но определенно проблема, которую вы должны учитывать.

@reZach Я не вижу причин, по которым window.require более безопасен, если честно. Кстати, есть более грязные способы сделать это.

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

@ marc2332 ни то, ни

Спасибо reZach! Я думаю, люди быстрее поймут твой подход

или позже

Марк Эльфинстон
www.oxfordsourceltd.com

Привет, Зак, поддерживает ли решение contextBridge прототипы?

Мой исходный код с contextIsolation: false просто прикрепил функцию к window . Эта функция возвращает Promise<MediaStream> однако в документации contextBridge говорится, что прототипы отброшены, и когда я использовал contextBridge я получаю пустой объект.

Есть ли способ поддержать прототипы, но при этом обеспечить contextIsolation: true ?

Привет, Зак, поддерживает ли решение contextBridge прототипы?

Мой исходный код с contextIsolation: false просто прикрепил функцию к window . Эта функция возвращает Promise<MediaStream> однако в документации contextBridge говорится, что прототипы отброшены, и когда я использовал contextBridge я получаю пустой объект.

Есть ли способ поддержать прототипы, но при этом обеспечить contextIsolation: true ?

Нет, это не так. Я пытался найти комментарий одного из членов команды, который я помню, о том, что это предполагаемое поведение, но не смог. Лучшее, что я могу сказать, это то, что в Electron v8 ( pr 20214 ) были внесены изменения в сериализацию объектов в IPC (эффективно, когда изоляция контекста включена), что прекратит поддержку (в настоящее время не рекомендуется, будет удалено в будущих версиях) для функций или прототипы.

ПРИМЕЧАНИЕ. Объекты, которые не могут быть сериализованы с помощью алгоритма структурированного клонирования V8, такие как функции, объекты DOM, специальные объекты Node / Electron, такие как process.env или WebContents, или любые объекты, содержащие такие элементы, будут сериализованы со старой базой :: Value- алгоритм на основе. Однако такое поведение устарело и вызовет исключение, начиная с Electron 9.

Вероятно, это не те новости, которые вы хотели услышать, но я надеюсь, что помог.

Привет, Зак, поддерживает ли решение contextBridge прототипы?
Мой исходный код с contextIsolation: false просто прикрепил функцию к window . Эта функция возвращает Promise<MediaStream> однако в документации contextBridge говорится, что прототипы отброшены, и когда я использовал contextBridge я получаю пустой объект.
Есть ли способ поддержать прототипы, но при этом обеспечить contextIsolation: true ?

Нет, это не так. Я пытался найти комментарий одного из членов команды, который я помню, о том, что это предполагаемое поведение, но не смог. Лучшее, что я могу сказать, это то, что в Electron v8 ( pr 20214 ) были внесены изменения в сериализацию объектов в IPC (эффективно, когда изоляция контекста включена), что прекратит поддержку (в настоящее время не рекомендуется, будет удалено в будущих версиях) для функций или прототипы.

ПРИМЕЧАНИЕ. Объекты, которые не могут быть сериализованы с помощью алгоритма структурированного клонирования V8, такие как функции, объекты DOM, специальные объекты Node / Electron, такие как process.env или WebContents, или любые объекты, содержащие такие элементы, будут сериализованы со старой базой :: Value- алгоритм на основе. Однако такое поведение устарело и вызовет исключение, начиная с Electron 9.

Вероятно, это не те новости, которые вы хотели услышать, но я надеюсь, что помог.

Это прискорбно, но спасибо за быстрый ответ.

Я выполнил описанные выше шаги (предоставленные @reZach и @aplum), и я передаю информацию из моего client.js в preload.js в main.js .

Когда все работает локально, связь работает, но когда я запускаю electron-builder , связь прерывается. Например, использование setBadge() прекрасно работает при локальном запуске. Я могу взять счетчик из файла client.js и увидеть, как он передается в contextBridge а затем в main.js и успешно устанавливает app.dock.setBadge(count) . Есть ли что-то, чего мне не хватает в связи в Electron после того, как он был построен с помощью 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 , я не сопровождаю группу электронов, но этот вопрос лучше подходит для

Я более склонен думать, что это проблема с кодом, а не со сборщиком электронов, когда я подумал об этом.

Я более склонен думать, что это проблема с кодом, а не со сборщиком электронов, когда я подумал об этом.

Я уверен, что мне не хватает какого-то ключевого элемента, который заставил бы все это работать так, как ожидалось. Благодарю за ваш ответ. Я открыл проблему в репо electron-builder , но она не получила поддержки.

Я более склонен думать, что это проблема с кодом, а не со сборщиком электронов, когда я подумал об этом.

Я уверен, что мне не хватает какого-то ключевого элемента, который заставил бы все это работать так, как ожидалось. Благодарю за ваш ответ. Я открыл проблему в репо electron-builder , но она не получила поддержки.

Вы создали репозиторий MVP со своей проблемой? Иногда это помогает, и в процессе вы понимаете, что происходит.

@Amthieu и @CiriousJoker, я люблю вас обоих, спасибо.

  1. пряжа добавить -D настроить-cra реагировать-приложение-перемонтировать
  2. измените package.json, чтобы использовать стартовое приложение, перенастроенное реакцией: "start": "BROWSER = none start-up-app-rewired",
  3. создайте файл config-overrides в корневом каталоге приложения React:
    `const {переопределить} = требовать ('настроить-cra');

function addRendererTarget (config) {
config.target = 'электронный рендерер';

вернуть конфиг;
}

module.exports = override (addRendererTarget); `

  1. теперь вы можете импортировать {ipcRenderer} из "электрона" :)

Я использую vuejs и просто добавляю код в vue.config.js

module.exports = {
"transpileDependencies": [
"vuetify"
],
pluginOptions: {
electronicBuilder: {
nodeIntegration: true
}
}
}

@genilsonmm Святое дерьмо, которое сработало! Я схожу с ума последние 3 часа. Спасибо!!
Конкретная часть того, что сработало для меня, была
`` javascript
pluginOptions: {
electronicBuilder: {
nodeIntegration: true
}
},

"transpileDependencies": [
"vuetify"
],

это не работает на меня ... но window.require ('electronic') работает хорошо

Для всех, у кого есть проблема: "window.require - это не функция"

Вы должны создать сценарий предварительной загрузки на электронном носителе.

  1. Создайте файл с именем preload.js в каталоге, в котором находится основной скрипт деэлектрона, поместите в него этот код:

    window.require = требуется;

  2. Перейдите к основному скрипту вашего электронного устройства и введите его в код, в котором вы создаете окно:

    win = new BrowserWindow ({
    ширина: 1280,
    высота: 720,
    webPreferences: {
    nodeIntegration: ложь,
    предварительная загрузка: __dirname + '/preload.js'
    },

    })
    Таким образом, вы предварительно загрузите скрипт, это решило проблему для меня. Надеюсь и на тебя :)

@reZach ваше решение сработало для меня, но я заметил ошибку, о которой другие люди могут споткнуться (потому что я это сделал, ух!):

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

вы определяете свою функцию обратного вызова как «func», но затем вызываете «fn», если вы измените ее, она будет работать именно так, как вы ее описали.
Большое спасибо за подробный пост: +1:

@genilsonmm Если я поставлю vue.config.js:

pluginOptions: {
electronicBuilder: {
nodeIntegration: правда,

Я получаю сообщение об ошибке «Требование не определено»:

image

Если я прокомментирую строку «nodeIntegration: true», сообщение об ошибке исчезнет, ​​и приложение заработает.

Что это означает?

@marcoippolito Я считаю, что это означает, что связанный с веб-пакетом код пытается использовать require который является методом разрешения модуля node js. Поскольку это недоступно, если вы отключите интеграцию узлов, код не может выполняться. Что вам нужно сделать, так это изменить конфигурацию вашего веб-пакета для целевых браузеров ( var - это цель, если я правильно помню) и убедиться, что ни один из ваших кодов не использует node apis и ошибка должна исчезнуть.

Для тех, кто использует TypeScript, есть хороший пример в официальных примерах 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 Святое дерьмо, которое сработало! Я схожу с ума последние 3 часа. Спасибо!!
Конкретная часть того, что сработало для меня, была

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

正 解 , 标记

У меня тоже есть эта проблема с включенной NodeIntegration. Оба window.require и require не работают.

+ Это происходит только с реакцией, а не с простым электроном

Была ли эта страница полезной?
0 / 5 - 0 рейтинги