Учебник по Electron.js

Процессы Main и Renderer

В приложении создаются процессы двух типов: Main и Renderer. Процесс Main является основным процессом, в котором можно создавать окна. В процессах Renderer обрабатываются Web-страницы. Таких процессов может быть много и они все изолированы друг от друга. В процессе Renderer мы не имеем возможности создавать окна. Чтобы определить тип процесса посмотрите на результат вывода метода log() объекта console. В процессе Main данные выводятся в консоль командной строки, тогда как в процессе Renderer данные отображаются на вкладке Console панели Инструменты разработчика.

Передача данных между процессами

Итак, процессы изолированы друг от друга. Чтобы процессы могли взаимодействовать, следует воспользоваться объектами ipcMain и ipcRenderer, которые позволяют посылать и обрабатывать события. Сгенерировать событие в процессе Renderer можно с помощью метода send() объекта ipcRenderer:

const { ipcRenderer } = require('electron');
ipcRenderer.send(<Тип события>[, ...<Данные>]);

В первом параметре указывается тип события в виде строки. В последующих параметрах передаются произвольные данные через запятую. Пример:

let data = document.getElementById('txt1').value;
ipcRenderer.send('event-send-data', data);

После генерации событие передается в процесс Main и может быть обработано с помощью методов on() и once() объекта ipcMain:

const { ipcMain } = require('electron');
ipcMain.on(<Тип события>, <Обработчик>);
ipcMain.once(<Тип события>, <Обработчик>);

В первом параметре указывается тип события в виде строки, а во втором — ссылка на функцию, которая будет вызвана при генерации события. Функция в первом параметре принимает объект события, а через последующие параметры доступны переданные данные:

ipcMain.on('event-send-data', (e, data) => {
});

Для отправки события из процесса Main в процесс Renderer, следует воспользоваться свойством webContents объекта окна. Событие генерируется с помощью метода send():

win.webContents.send('win-event-send-data', data);

Если нужно отправить событие внутри обработчика тому же процессу Renderer, то можно воспользоваться следующим кодом:

ipcMain.on('event-create-window', (e) => {
   // ...
   e.sender.send('event-opened-window', 'Окно создано');
});

Можно также воспользоваться методом reply():

ipcMain.on('event-create-window', (e) => {
   // ...
   e.reply('event-opened-window', 'Окно создано');
});

Если событие генерируется с помощью метода sendSync(<Тип события>[, ...<Данные>]), то операция будет выполнена синхронно и мы можем получить ответ, который метод вернет:

let result = ipcRenderer.sendSync('event-create-window');
console.log(result);

Внутри обработчика ответ следует присвоить свойству returnValue:

ipcMain.on('event-create-window', (e) => {
   // ...
   e.returnValue = 'Окно создано';
});

Чтобы можно было использовать объект ipcRenderer нужно при создании окна в разделе webPreferences добавить опцию nodeIntegration со значением true и опцию contextIsolation со значением false:

win = new BrowserWindow({
   width: 800,
   height: 600,
   webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
   }
});

Пример обмена данными

Давайте создадим программу, позволяющую обмениваться данными между процессами. При нажатии кнопки в первом окне сгенерируем событие event-create-window. Внутри обработчика события создадим и откроем новое окно. В этом окне добавим текстовое поле и кнопку. При нажатии кнопки отправим данные, введенные пользователем в текстовое поле, и получим их в первом окне.

Содержимое основного файла приложения main.js приведено в листинге 1.5, файла index.htm — в листинге 1.6, файла test1.js — в листинге 1.7, файла test.html — в листинге 1.8, файла test2.js — в листинге 1.9.

Листинг 1.5. Содержимое файла C:\book\e1\main.js

const { app, BrowserWindow, ipcMain } = require('electron');
let win = null;
function createWindow() {
   win = new BrowserWindow({
      width: 800,
      height: 600,
      webPreferences: {
         nodeIntegration: true,
         contextIsolation: false
      }
   });
   win.loadFile('index.html');
   win.webContents.openDevTools();
   win.on('closed', () => {
      win = null;
      app.quit();
   });
}
app.whenReady().then( () => {
   createWindow();
   app.on('activate', () => {
      if (BrowserWindow.getAllWindows().length === 0) {
         createWindow();
      }
   });
} );
app.on('window-all-closed', () => {
   if (process.platform !== 'darwin') {
      app.quit();
   }
});
ipcMain.on('event-create-window', (e) => {
   const w = new BrowserWindow({
      width: 800,
      height: 600,
      webPreferences: {
         nodeIntegration: true,
         contextIsolation: false
      }
   });
   w.loadFile('test.html');
   w.webContents.openDevTools();
   e.sender.send('event-opened-window', 'Окно создано');
});
ipcMain.on('event-send-data', (e, data) => {
   if (win) {
      win.webContents.send('win-event-send-data', data);
   }
});
console.log('Process Main');

Листинг 1.6. Содержимое файла C:\book\e1\index.html

<!doctype html>
<html lang="ru">
<head>
   <meta charset="utf-8">
   <meta http-equiv="Content-Security-Policy"
         content="default-src 'self'">
   <title>Процессы Main и Renderer</title>
</head>
<body>
   <h1>Renderer1</h1>
   <button type="button" id="btnOpen">Открыть окно</button>
   <script src="test1.js"></script>
</body>
</html>

Листинг 1.7. Содержимое файла C:\book\e1\test1.js

const { ipcRenderer } = require('electron');
console.log('Process Renderer1');
document.getElementById('btnOpen').addEventListener('click', () => {
   ipcRenderer.send('event-create-window');
});
ipcRenderer.on('win-event-send-data', (e, data) => {
   console.log(data);
});
ipcRenderer.on('event-opened-window', (e, data) => {
   console.log(data);
});

Листинг 1.8. Содержимое файла C:\book\e1\test.html

<!doctype html>
<html lang="ru">
<head>
   <meta charset="utf-8">
   <meta http-equiv="Content-Security-Policy"
         content="default-src 'self'">
   <title>Процесс Renderer</title>
</head>
<body>
   <h1>Renderer2</h1>
   <input type="text" id="txt1">
   <button type="button" id="btn1">Передать данные</button>
   <script src="test2.js"></script>
</body>
</html>

Листинг 1.9. Содержимое файла C:\book\e1\test2.js

const { ipcRenderer } = require('electron');
console.log('Process Renderer2');
document.getElementById('btn1').addEventListener('click', (e) => {
   let data = document.getElementById('txt1').value;
   ipcRenderer.send('event-send-data', data);
});