Я создаю приложение Electron для собственных целей. Моя проблема в том, что когда я использую функции узла внутри своей HTML-страницы, возникает ошибка:

'require ()' не определен.

Есть ли способ использовать функции Node на всех моих HTML-страницах? Если возможно, дайте мне пример, как это сделать, или дайте ссылку. Вот переменные, которые я пытаюсь использовать на своей HTML-странице:

  var app = require('electron').remote; 
  var dialog = app.dialog;
  var fs = require('fs');

, и это значения, которые я использую во всех своих окнах HTML в Electron.

Ответы (9)

Начиная с версии 5, значение по умолчанию для nodeIntegration изменено с true на false. Вы можете включить его при создании окна браузера:

app.on ('готово', () => {
    mainWindow = new BrowserWindow ({
        webPreferences: {
            nodeIntegration: правда,
            contextIsolation: false,
        }
    });
});

Надеюсь, этот ответ привлечет некоторое внимание, потому что подавляющее большинство ответов здесь оставляет большие дыры в безопасности в вашем электронном приложении. Фактически , этот ответ - это, по сути, то, что вы должны делать, чтобы использовать require () в своих электронных приложениях. (Есть только новый электронный API, который делает его немного чище в v7).

Я написал подробное объяснение / решение в github, используя самые последние электронные API, как вы можете require () что-то, но я кратко объясню здесь, почему вы должны следовать подход с использованием сценария предварительной загрузки, contextBridge и ipc.

Проблема

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

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

Как выглядит проблема

Эта проблема проявляется, когда вы (любой из следующих):

  1. Есть nodeIntegration: true включено
  2. Используйте удаленный модуль

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

Наше решение

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

В последних версиях (7+) Electron это работает: на стороне рендерера мы устанавливаем привязки ipcRenderer, а на основной стороне устанавливаем привязки ipcMain . В привязках ipcMain мы настраиваем методы прослушивателя, которые используют модули, которые мы require (). Это нормально, потому что наш основной процесс может требовать всего, что ему нужно.

We use the contextBridge to pass the ipcRenderer bindings to our app code (to use), and so when our app needs to use the required modules in main, it sends a message via IPC (inter-process-communication) and the main process runs some code, and we then send a message back with our result.

Примерно, вот что вы хотите сделать.

main.js

const {
  приложение,
  BrowserWindow,
  ipcMain
} = require ("электрон");
const path = require ("путь");
const fs = require ("fs");

// Сохраняем глобальную ссылку на объект окна, если вы этого не сделаете, окно будет
// автоматически закрывается, когда объект JavaScript собирается сборщиком мусора.
пусть победит;

асинхронная функция createWindow () {

  // Создаем окно браузера.
  win = new BrowserWindow ({
    ширина: 800,
    высота: 600,
    webPreferences: {
      nodeIntegration: false, // значение по умолчанию после Electron v5
      contextIsolation: true, // защита от загрязнения прототипа
      enableRemoteModule: false, // выключить удаленный
      preload: path.join (__ dirname, "preload.js") // использовать сценарий предварительной загрузки
    }
  });

  // Загружаем приложение
  win.loadFile (path.join (__ dirname, "dist / index.html"));

  // остальной код ..
}

app.on ("готово", createWindow);

ipcMain.on ("toMain", (событие, аргументы) => {
  fs.readFile ("путь / к / файлу", (ошибка, данные) => {
    // Что-то делаем с содержимым файла

    // Отправляем результат обратно в процесс рендеринга
    win.webContents.send ("fromMain", responseObj);
  });
});

preload.js

const {
    contextBridge,
    ipcRenderer
} = require ("электрон");

// Предоставляем защищенные методы, которые позволяют процессу рендеринга использовать
// ipcRenderer без раскрытия всего объекта
contextBridge.exposeInMainWorld (
    "api", {
        send: (канал, данные) => {
            // добавляем каналы в белый список
            пусть validChannels = ["toMain"];
            if (validChannels.includes (канал)) {
                ipcRenderer.send (канал, данные);
            }
        },
        получить: (канал, функция) => {
            пусть validChannels = ["fromMain"];
            if (validChannels.includes (канал)) {
                // Намеренно удаляем событие, так как оно включает `отправителя`
                ipcRenderer.on (канал, (событие, ... аргументы) => func (... аргументы));
            }
        }
    }
);

index.html




    
     Заголовок 


    <сценарий>
        window.api.receive ("fromMain", (данные) => {
            console.log (`Получены $ {данные} от основного процесса`);
        });
        window.api.send ("toMain", "некоторые данные");
    


Заявление об ограничении ответственности

Я автор secure-electronic-template, безопасного шаблона для создания электронных приложений. Мне небезразлична эта тема, и я работаю над ней несколько недель (на данный момент).

Используете ли вы nodeIntegration: false при инициализации BrowserWindow? Если это так, установите значение true (значение по умолчанию true).

И включите ваши внешние скрипты в HTML следующим образом (не как ):


Наконец, я заставил это работать. Добавьте этот код в свой HTML-документ Script Element.

Извините за поздний ответ. Для этого я использую приведенный ниже код.

window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;

И используйте nodeRequire вместо require.

Работает нормально.

Вы должны включить nodeIntegration в webPreferences, чтобы использовать его. см. ниже,

const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
})
win.show()

Произошли критические изменения api в электроне 5.0 (Объявление в репозитории). В последних версиях nodeIntegration по умолчанию установлено значение false.

Docs Из-за интеграции Electron с Node.js в DOM вставлены некоторые дополнительные символы, такие как module, exports, require. Это вызывает проблемы для некоторых библиотек, поскольку они хотят вставить символы с одинаковыми именами. Чтобы решить эту проблему, вы можете отключить интеграцию узлов в Electron:

Но если вы хотите сохранить возможность использования API-интерфейсов Node.js и Electron, вам необходимо переименовать символы на странице перед включением других библиотек:


    
    

Все, что я хотел сделать, это потребовать js-файл на моей html-странице из-за учебника, которому я следовал. Однако я намерен использовать удаленные модули, поэтому безопасность была превыше всего. Я изменил ответ Майкла там, поэтому я публикую его исключительно для тех, кто часами искал безопасную альтернативу запросу, как я. Если код неверен, не стесняйтесь указать на это.

main.js

const electron = require('electron');
const app=electron.app;
const BrowserWindow=electron.BrowserWindow;
const ipcMain=electron.ipcMain;

const path=require('path');
const url=require('url');

let win;

function createWindow(){
    win=new BrowserWindow({
        webPreferences:{
            contextIsolation: true,
            preload: path.join(__dirname, "preload.js")
        }
    });
    win.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file',
        slashes: true
    }));

    win.on('close', function(){
        win=null
    });
}

app.on('ready', createWindow);

preload.js

const electron=require('electron');
const contextBridge=electron.contextBridge;

contextBridge.exposeInMainWorld(
    "api", {
        loadscript(filename){
            require(filename);
        }
    }
);

index.html



    
        Hello World App
    
    
        

Hello World

index.js

const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
    console.log('button clicked');
});

Мне особенно любопытно узнать, представляет ли это опасность для безопасности. Спасибо.

По соображениям безопасности вы должны сохранить nodeIntegration: false и использовать сценарий предварительной загрузки, чтобы предоставлять только то, что вам нужно от API Node / Electron для процесса (представления) средства визуализации через переменную окна. Из Электронной документации:

Скрипты предварительной загрузки по-прежнему имеют доступ к require и другим функциям Node.js


Пример

main.js

const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: path.join(app.getAppPath(), 'preload.js')
  }
})

preload.js

const { remote } = require('electron');

let currWindow = remote.BrowserWindow.getFocusedWindow();

window.closeCurrentWindow = function(){
  currWindow.close();
}

renderer.js

let closebtn = document.getElementById('closebtn');

closebtn.addEventListener('click', (e) => {
  e.preventDefault();
  window.closeCurrentWindow();
});

Во-первых, решение @Sathiraumesh оставляет ваше электронное приложение с огромной проблемой безопасности. Представьте, что ваше приложение добавляет некоторые дополнительные функции в messenger.com, например, значок на панели инструментов будет меняться или мигать, когда у вас есть непрочитанное сообщение. Итак, в вашем файле main.js вы создаете новое окно BrowserWindow следующим образом (обратите внимание, я намеренно неправильно написал messenger.com):

app.on ('готово', () => {
    const mainWindow = new BrowserWindow ({
        webPreferences: {
            nodeIntegration: true
        }
    });
    mainWindow.loadURL (`https://messengre.com`);
});

Что делать, если messengre.com - это вредоносный веб-сайт, который хочет нанести вред вашему компьютеру. Если вы установите nodeIntegration: true, этот сайт имеет доступ к вашей локальной файловой системе и может выполнить это:

require ('child_process'). Exec ('rm -r ~ /');

И вашего домашнего каталога больше нет.

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

// main.js
app.on ('готово', () => {
    const mainWindow = new BrowserWindow ({
        webPreferences: {
            предварительная загрузка: `$ {__ dirname} / preload.js`
        }
    });
    mainWindow.loadURL (`https://messengre.com`);
});
// preload.js
window.ipcRenderer = требуется ('электрон'). ipcRenderer;
// index.html
<сценарий>
    window.ipcRenderer.send ('канал', данные);

Ужасно messengre.com не может удалить всю вашу файловую систему.

Похоже, безопасность Electron развивалась следующим образом (источник).

Электрон 1 nodeIntegration по умолчанию true

Renderer имеет полный доступ к Node API - огромная угроза безопасности, если Renderer загружает удаленный код.

Electron 5 nodeIntegration по умолчанию false

Если установлено значение false, сценарий предварительной загрузки используется для предоставления определенного API для средства визуализации. (Скрипт предварительной загрузки всегда имеет доступ к API-интерфейсам узла независимо от значения nodeIntegration)

// preload.js
window.api = {
    deleteFile: f => require ('fs'). unlink (f)
}

Electron 5 contextIsolation по умолчанию true (на самом деле все еще по умолчанию false в Electron 11)

Это приводит к тому, что сценарий предварительной загрузки запускается в отдельном контексте. Вы больше не можете делать window.api = .... Теперь вам нужно сделать:

// preload.js
const {contextBridge} = require ('электрон')

contextBridge.exposeInMainWorld ('api', {
    deleteFile: f => require ('fs'). unlink (f)
})

Electron 6 require ()ing node встроенные в песочнице средства визуализации больше не загружает неявно удаленную версию

Если для параметра sandbox установлено значение true, необходимо выполнить:

// preload.js
const {contextBridge, remote} = require ('электрон')

contextBridge.exposeInMainWorld ('api', {
    deleteFile: f => remote.require ('fs'). unlink (f)
})

Electron 10 enableRemoteModule по умолчанию false (remote модуль устарел в Electron 12)

Модуль remote используется, когда вам нужно получить доступ к API-интерфейсам узла из изолированного средства визуализации (как в примере выше); или когда вам нужно получить доступ к API-интерфейсам Electron, которые доступны только для основного процесса (например, диалогового окна, меню). Без remoteвам нужно будет написать явные обработчики IPC, как показано ниже.

// preload.js
const {contextBridge, ipcRenderer} = require ('электрон')

contextBridge.exposeInMainWorld ('api', {
    displayMessage: text => ipcRenderer.invoke ("displayMessage", текст)
})

//main.js
const {ipcMain, dialog} = require ('электрон')

ipcMain.handle ("displayMessage", text => dialog.showMessageBox (текст))

Electron 10 устарело nodeIntegration флаг (удален в Electron 12)

Рекомендация

Всегда устанавливать {nodeIntegration: false, contextIsolation: true, enableRemoteModule: false}.

Для максимальной безопасности установите {sandbox: true}. Ваш сценарий предварительной загрузки должен будет использовать IPC для вызова основного процесса для выполнения всего.

Если sandbox ложно, ваш сценарий предварительной загрузки может напрямую обращаться к Node API, как в require ('fs'). ReadFile. Вы в безопасности, пока не сделаете это:

// плохо
contextBridge.exposeInMainWorld ('api', {
    readFile: требуется ('fs'). readFile
})

2022 WebDevInsider