Electron: خطأ أثناء استيراد الإلكترون المتفاعل | استيراد {ipcRenderer} من "إلكترون"

تم إنشاؤها على ٣ يوليو ٢٠١٧  ·  65تعليقات  ·  مصدر: electron/electron

لقد أنشأت تطبيق تفاعل بسيط مع تطبيق create-response-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 أن هذه مشكلة شائعة عند محاولة استيراد الإلكترون.

شكرا للمساعدة

التعليق الأكثر فائدة

تضمين التغريدة

لقد وجدت الحل في المشكلة رقم 7300 إذا كان بإمكانه مساعدة أي شخص.

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

يرجى ملاحظة أن هذا سيعمل عند تشغيل تطبيق Electron ، ولكن إذا كنت ترغب فقط في اختبار كود React الخاص بك داخل المتصفح ، فسيظل يتعطل (لم يتم تعريف window.require في المتصفح كما هو الحال في Electron).

ال 65 كومينتر

تستخدم CRA حزمة الويب التي تعبث مع تحميل الوحدة القياسي (بما في ذلك fs).

أوصي بالبحث في وضع الإلكترون لحزمة الويب والإخراج من CRA

مشكلات GitHub مخصصة لطلبات الميزات وتقارير الأخطاء ، ويجب توجيه الأسئلة حول استخدام Electron إلى المجتمع أو إلى قناة Slack .

تضمين التغريدة

لقد وجدت الحل في المشكلة رقم 7300 إذا كان بإمكانه مساعدة أي شخص.

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

يرجى ملاحظة أن هذا سيعمل عند تشغيل تطبيق Electron ، ولكن إذا كنت ترغب فقط في اختبار كود React الخاص بك داخل المتصفح ، فسيظل يتعطل (لم يتم تعريف window.require في المتصفح كما هو الحال في Electron).

إذا كنت تريد الوصول إلى app.quit () ، فيمكنك استخدام هذا:

const {app} = window.require ('إلكترون'). remote ؛

ربما يساعد شخصًا ما ...

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 الخاصة بي. لست متأكدًا مما إذا كان ذلك ممكنًا حتى عندما يتم تحميل تطبيقي من الويب. أي اقتراحات؟

تضمين التغريدة

في نفس المركب ... هل وجدت الحل؟

لا. أنا متأكد من أنه لا يمكن تحميل 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 ليست وظيفة.

تضمين التغريدة

اعتذارات - كان يجب أن تضيف من حيث أحصل على Electron from ، وقمت بتحرير مثال الكود الخاص بي بالرابط إلى: https://github.com/cheton/is-electron

تضمين التغريدة
هل هناك حل الآن

بالنسبة لي العمل فقط إذا كان nodeIntegration هو true ؛

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

تعمل بشكل رائع على حل HemalR !

الآن كيف ترسل من إلكترون إلى رد فعل؟

حاولت مع
على جانب الإلكترون

 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} = تتطلب ('إلكترون')
// حسن:
const {ipcRenderer} = window.require ('إلكترون')

(يوجد أدناه أيضًا في نفس الملف الإلكترون "pong-Demo" ، والذي يثبت أنه يعمل نوعًا ما)

ربما يكون جديرًا بالملاحظة: حتى عند القيام بذلك بشكل خاطئ ، لا ينمو حجم الحزمة (مقارنة بغير طلب الإلكترون. هذا هو الأول وحتى الآن استيراد الإلكترون من جانب العرض) بحجم الإلكترون بأكمله أو ما شابه ، ولكن فقط بحوالي 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 كان يقيم بيان الطلب الخاص بي في وقت التجميع.

تضمين التغريدة

لقد وجدت الحل في المشكلة رقم 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 الحل الخاص بك يعمل بشكل رائع. لست بحاجة إلى Webpack أو Browserfy لاستخدام IpcRenderer في مشروع React الخاص بي. شكرا جزيلا مرة أخرى: د

للطباعة واستخدام 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 ، وأتخيل رد الفعل والزاوية

فقط أضف الهدف: "عارض الإلكترون" في تكوينات حزمة الويب.
تصدير الافتراضي {
...
الهدف: "جهاز عرض الإلكترون"
...
}

مرحبًا ، أستخدم CRA + Electron. وأنا أقوم ببناء مشروعي عن طريق "العارض الإلكتروني" الهدف. إنه يعمل بشكل جيد أثناء تحميل الملفات في مجلد الإنشاء ، ولكنه يحدث خطأ عند تطوير وتحميل url localhost:3000 . أعتقد أن السبب هو أنني أستخدم node api و electron api في مكونات التفاعل. أي شخص لديه الحل؟ شكرا.

TypeError: fs.existsSync is not a function

ياه ، واجهة برمجة تطبيقات 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 و electron api في مكونات التفاعل. أي شخص لديه الحل؟ شكرا.

TypeError: fs.existsSync is not a function

فقط أضف الهدف: "عارض الإلكترون" في تكوينات حزمة الويب.
تصدير الافتراضي {
...
الهدف: "جهاز عرض الإلكترون"
...
}

إنه يعمل لدي أيضا.

ها هو إصدار أدواتي:

  • حزمة الويب: "3.8.1"
  • الإلكترون: "4.2.1"

أعتقد أن electron-renderer فقط لحل مشكلة الإلكترون ، لست بحاجة إلى استخدام بناء جملة سلكي مثل window.required ، بل إنه فقد الكتابة!

بالنسبة لي ، عملت تحديد الهدف: "عرض الإلكترون" لحزمة الويب و nodeIntegration: صحيح في خيارات BrowserWindow
أنا أستخدم حزمة ويب + إلكترون + تتفاعل مع إصدارات اليوم الأخيرة

أقوم بتشغيل تطبيق create-رد فعل باستخدام TypeScript an Electron. لقد اتبعت هذه التعليمات الجيدة جدًا للإعداد. ولكن بعد ذلك واجهت هذا الخطأ أيضًا. الحل الذي يناسبني هو مجموع الأشياء التي قيلت في هذا الموضوع:

  1. اجعل رد الفعل النصي يستخدم "electron-renderer" كـ target . أستخدم rescripts مع rescript-env لهذا الغرض.

package.json

  "rescripts": [
    "env"
  ],

.rescriptsrc.js

module.exports = [تتطلب.resolve ("./ webpack.config.js")] ؛

webpack.config.js :

module.exports = config => {
  config.target = "electron-renderer";
  return config;
};
  1. إضافة nodeIntegration
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());

تضمين التغريدة
يجب أن يعمل هذا أيضًا:

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

استيراد ما تحتاجه داخل التطبيق الخاص بك. أقوم باستيراد وحدة shell إلى مكوِّني كما يلي:

import { shell } from 'electron'

لأي شخص لا يزال يعاني من نفس المشكلة. هذا هو أفضل حل وجدته حتى الآن

شبيبة
BrowserWindow الجديد ({
تفضيلات الويب: {
تكامل العقدة: صحيح
}
}) ؛

آمل أن يتم ملاحظة هذا التعليق ، لأن الكثير من الأشخاص يسألون عن استيراد fs أو ipcRenderer في تطبيقاتك. إنها حاجة شائعة لتطبيقات الإلكترون ، لكنني لم أجد أن الكثير من الناس قد فهموها بشكل صحيح ، ويستخدمون أنماطًا قديمة. tl؛ dr - هناك ثغرة أمنية إذا لم تقم باستيراد وحدة العقدة (على سبيل المثال fs ) أو وحدة الإلكترون (على سبيل المثال ipcRenderer ) بالطريقة الصحيحة. إذا كنت تستخدم تطبيقك لنفسك فقط ، فمن المحتمل أن تكون آمنًا ، ولكن إذا أردت في أي وقت مشاركة تطبيقك أو بيعه ، فيجب عليك القراءة مسبقًا.

هدفنا

قبل الخوض في الحل ، من المهم أن نفهم لماذا نقوم بهذا في المقام الأول. تسمح لنا تطبيقات Electron بتضمين وحدات العقدة في تطبيقاتنا ، مما يمنحها قوة مذهلة ، ولكن مخاوف أمنية. نريد السماح لتطبيقنا باستخدام ميزات نظام التشغيل الأصلي (أي العقدة) ، لكننا لا نريد إساءة استخدامها .

كما ذكر 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 الخاصة بي أقرب إلى واجهة 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 ، هذا لا يتعلق بالإلكترون ، هل ربما لديك مستودع جيثب خاص بك حيث يمكن أن تستمر المحادثة؟ (يبدو أن المسار الخاص بك في 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 ، أو كائنات العقدة / الإلكترون الخاصة مثل process.env أو محتويات الويب ، أو أي كائنات تحتوي على مثل هذه العناصر باستخدام القاعدة القديمة: القيمة- الخوارزمية القائمة. ومع ذلك ، تم إهمال هذا السلوك وسيؤدي إلى استثناء يبدأ بـ Electron 9.

ربما لم تكن هذه هي الأخبار التي أردت سماعها ولكني آمل أن أكون قد ساعدت.

مرحبًا زاك ، هل يدعم الحل contextBridge النماذج الأولية؟
الكود الأصلي الخاص بي الذي يحتوي على contextIsolation: false أرفق ببساطة دالة في window . تقوم هذه الوظيفة بإرجاع Promise<MediaStream> لكن مستندات contextBridge تشير إلى أنه تم إسقاط النماذج الأولية وعندما استخدمت contextBridge أتلقى كائنًا فارغًا.
هل هناك طريقة لدعم النماذج الأولية مع ضمان contextIsolation: true ؟

لا ، لا. كنت أحاول العثور على تعليق أتذكره من قبل أحد أعضاء الفريق مفاده أن هذا سلوك مقصود لكنني لم أستطع. أفضل ما يمكنني قوله هو في Electron v8 ، ( pr 20214 ) الذي أحدث تغييرات في تسلسل الكائنات داخل IPC (بشكل فعال عند تشغيل عزل السياق) والذي سيؤدي إلى إسقاط الدعم (تم إهماله حاليًا ، سيتم إزالته في الإصدارات المستقبلية) للوظائف أو النماذج.

ملاحظة: سيتم إجراء تسلسل للكائنات غير القابلة للتسلسل باستخدام خوارزمية استنساخ الهيكلية الخاصة بـ V8 ، مثل الوظائف ، أو كائنات DOM ، أو كائنات العقدة / الإلكترون الخاصة مثل process.env أو محتويات الويب ، أو أي كائنات تحتوي على مثل هذه العناصر باستخدام القاعدة القديمة: القيمة- الخوارزمية القائمة. ومع ذلك ، تم إهمال هذا السلوك وسيؤدي إلى استثناء يبدأ بـ 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 repo مع مشكلتك؟ يساعد ذلك في بعض الأحيان وفي هذه العملية تدرك ما يحدث.

Amthieu و شكرًا لكما .

  1. إضافة الغزل - تخصيص- cra- رد فعل التطبيق- إعادة توصيله
  2. قم بتعديل package.json لاستخدام تطبيق البدء التفاعلي: "start": "BROWSER = لا شيء يبدأ التفاعل-app-Rewired" ،
  3. إنشاء ملف "config-overrides" في دليل جذر التطبيق التفاعلي:
    "const {override} = تتطلب ('تخصيص-cra') ؛

الوظيفة addRendererTarget (config) {
config.target = 'الإلكترون العارض' ؛

عودة التكوين ؛
}

module.exports = override (addRendererTarget) ؛ `

  1. يمكنك استيراد {ipcRenderer} من "الإلكترون" الآن :)

أنا أستخدم vuejs وأضف الكود في vue.config.js

module.exports = {
"transpileDependencies": [
"vuetify"
] ،
pluginOptions: {
منشئ الإلكترون: {
تكامل العقدة: صحيح
}
}
}

genilsonmm حماقة المقدسة التي عملت! لقد كنت مجنونًا منذ 3 ساعات. شكرا!!
الجزء المحدد الذي نجح معي هو
`` جافا سكريبت
pluginOptions: {
منشئ الإلكترون: {
تكامل العقدة: صحيح
}
} ،

"transpileDependencies": [
"vuetify"
] ،

هذا لا يعمل علي ... لكن window.require ("الإلكترون") يعمل بشكل جيد

لجميع الأشخاص الذين لديهم مشكلة "window.require ليس وظيفة"

لديك لإنشاء برنامج نصي للتحميل المسبق على الإلكترون.

  1. قم بإنشاء ملف يسمى preload.js في الدليل حيث لديك البرنامج النصي الرئيسي de electron ، ضع هذا الرمز عليه:

    window.require = يتطلب ؛

  2. انتقل إلى البرنامج النصي الرئيسي للإلكترون الخاص بك واكتب هذا في الكود حيث تقوم بإنشاء النافذة:

    win = BrowserWindow جديد ({
    العرض: 1280 ،
    الارتفاع: 720 ،
    تفضيلات الويب: {
    العقدة التكامل: خطأ ،
    التحميل المسبق: __dirname + '/preload.js'
    } ،

    })
    مع هذا سوف تقوم بتحميل البرنامج النصي مسبقًا قبل كل شيء ، وهذا حل المشكلة بالنسبة لي. اتمنى لك ايضا :)

reZach لقد نجح الحل الخاص بك بالنسبة لي ، لكنني لاحظت وجود خطأ قد يتعثر فيه الآخرون (لأنني فعلت ذلك ، woops!):

        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: {
منشئ الإلكترون: {
العقدة التكامل: صحيح ،

تلقيت خطأ "طلب غير محدد":

image

إذا قمت بالتعليق على السطر "nodeIntegration: true" ، تختفي رسالة الخطأ ويعمل التطبيق.

ماذا يعني ذلك؟

marcoippolito أعتقد أن هذا يعني أن كود حزمة الويب المجمعة يحاول استخدام require وهي طريقة دقة وحدة العقدة js. نظرًا لأن هذا غير متاح إذا قمت بتعطيل تكامل العقدة ، فلا يمكن تنفيذ الكود. ما عليك القيام به هو تعديل webpack config إلى المتصفحات المستهدفة ( 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 والمطلوب لا يعمل.

+ يحدث هذا فقط مع الإلكترون المتفاعل ، وليس الإلكترون العادي

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات