В приложении создаются процессы двух типов: 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, файла test
2
.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);
});