Архитектура Electron

Electron можно представить как:

Многопроцессный контейнер приложений со встроенным браузерным движком (Chromium) и средой выполнения Node.js,
Настольные приложения, объединяющие "веб-страницы + системные возможности" и взаимодействующие через межпроцессное взаимодействие (IPC).

Его основные компоненты:

Роль Описание Аналогия
Главный процесс (main process) Мозг Electron, управляющий жизненным циклом приложения, создающий окна и вызывающий системные API Менеджер процессов ОС
Процесс рендеринга Среда веб-страницы (HTML, CSS, JS), запускаемая каждым окном Вкладка браузера
Скрипт предзагрузки Выполняется перед рендерингом, может связать API главного процесса для использования веб-страницами Безопасность «промежуточный слой»
IPC (межпроцессное взаимодействие) Канал связи между главным процессом и процессами рендеринга телефонная линия
Объект BrowserWindow «Контейнер окна», создаваемый главным процессом, который загружает веб-страницы внутри себя Окно браузера
Модуль приложения Управляет жизненным циклом приложения (запуск, выход) Главный контроллер

Главный процесс

Главный процесс — это «мозг» приложения Electron; у каждого приложения Electron есть ровно один главный процесс. Он отвечает за:

  • Создание и управление окнами приложения(Процесс рендеринга)
  • Обработка жизненного цикла приложения(Запуск, Выход, Переключение переднего/фонового режима)
  • Взаимодействие с нативными API операционной системы
  • Управление компонентами системного уровня, такими как меню и диалоговые окна

Пример

// main.js - пример главного процесса
const { app, BrowserWindow } = require('electron');

function createWindow() {
  // Создать окно браузера
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  });

  // Загрузить index.html приложения
  mainWindow.loadFile('index.html');
}

// Вызывается, когда Electron завершает инициализацию
app.whenReady().then(createWindow);

Процесс рендеринга

Процесс рендеринга отвечает за отображение пользовательского интерфейса; каждое окно Electron — это независимый процесс рендеринга:

  • Работает в среде браузера Chromium
  • Используйте HTML, CSS и JavaScript для создания интерфейсов
  • Каждое окно — это независимый процесс, и они не влияют друг на друга
  • Взаимодействует с главным процессом через IPC

Пример

<!-- index.html - пример процесса рендеринга -->
<!DOCTYPE html>
<html>
<head>
    <title>Моё приложение Electron</title>
</head>
<body>
    <h1>Привет, Electron!</h1>
    <button id="btn">Нажми меня</button>
   
    <script>
// JavaScript в процессе рендеринга
        document.getElementById('btn').addEventListener('click', () => {
alert('Кнопка была нажата!');
        });
    </script>
</body>
</html>

Предзагрузочные скрипты

Предзагрузочный скрипт — это "мост", соединяющий главный процесс и процесс рендеринга:

  • Выполняется до загрузки веб-страницы в процессе рендеринга
  • Имеет доступ к API Node.js и DOM
  • Безопасно предоставляйте API процессу рендеринга через contextBridge

Пример

// preload.js — пример preload-скрипта
const { contextBridge, ipcRenderer } = require('electron');

// Предоставить безопасный API процессу рендеринга
contextBridge.exposeInMainWorld('electronAPI', {
  showDialog: (message) => ipcRenderer.invoke('show-dialog', message)
});

Межпроцессное взаимодействие (IPC)

Шаблоны IPC-взаимодействия

IPC(межпроцессное взаимодействие)Является ядром взаимодействия Electron.

Поскольку главный процесс и процесс рендеринга независимы, данные должны передаваться через IPC.

Направление взаимодействия

Type Прослушивание главного процесса Отправлено процессом рендеринга
Процесс рендеринга → Главный процесс ipcMain.on(channel, handler) ipcRenderer.send(channel, data)
Главный процесс → Процесс рендеринга event.sender.send(channel, data) ipcRenderer.on(channel, callback)

Пример базового взаимодействия

Код главного процесса:

Пример

const { ipcMain, dialog } = require('electron');

// Прослушивание сообщений от процесса рендеринга
ipcMain.handle('show-dialog', async (event, message) => {
  const result = await dialog.showMessageBox({
    type: 'info',
    message: message,
    buttons: ['OK', 'Cancel']
  });
  return result;
});

Код процесса рендеринга:

Пример

// Обмен данными через API, предоставляемый preload-скриптом
document.getElementById('btn').addEventListener('click', async () => {
  const result = await window.electronAPI.showDialog('Привет, Electron!');
  console.log('Пользователь нажал:', result.response);
});

Преимущества и особенности архитектуры

Сравнение преимуществ

Особенности Electron Традиционная разработка настольных приложений
Технология разработки Веб-технологии (HTML/CSS/JS) Нативные языки (C++/C#/Java)
Кроссплатформенная поддержка Разработка один раз, запуск на нескольких платформах Требует отдельной разработки для каждой платформы
Эффективность разработки Высокая, использует существующую веб-экосистему Ниже, требует изучения специфичных для платформы технологий
Производительность Относительно низкая, потребляет больше ресурсов Высокая, нативная производительность
Размер установщика Большой (включает Chromium) меньше

Основные возможности

  1. Кроссплатформенная согласованность: Приложение выглядит и ведёт себя одинаково во всех операционных системах
  2. Использование веб-экосистемы: Можно напрямую использовать npm-пакеты и веб-фреймворки
  3. Быстрое прототипирование: На основе знакомых веб-технологий, короткий цикл разработки
  4. Богатые API: Предоставляет полный набор API для доступа к нативным возможностям системы

Примеры практического применения

Давайте создадим простое приложение файлового менеджера, чтобы продемонстрировать архитектуру Electron:

Главный процесс (main.js):

Пример

const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const fs = require('fs').promises;
const path = require('path');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 700,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true
    }
  });

  mainWindow.loadFile('index.html');
}

// Обработка чтения файла
ipcMain.handle('read-file', async (event, filePath) => {
  try {
    const content = await fs.readFile(filePath, 'utf-8');
    return { success: true, content };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Обработка выбора файла
ipcMain.handle('select-file', async () => {
  const result = await dialog.showOpenDialog(mainWindow, {
    properties: ['openFile'],
    filters: [
      { name: 'Текстовый файл', extensions: ['txt', 'md', 'js', 'html', 'css'] }
    ]
  });
 
  return result;
});

app.whenReady().then(createWindow);

Скрипт предзагрузки (preload.js):

Пример

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('fileAPI', {
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
  selectFile: () => ipcRenderer.invoke('select-file')
});

Процесс рендеринга (index.html):

Пример

<!DOCTYPE html>
<html>
<head>
    <title>Простой файловый менеджер</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 800px; margin: 0 auto; }
        button { padding: 10px 15px; margin: 5px; cursor: pointer; }
        #content { border: 1px solid #ccc; padding: 15px; margin-top: 10px;
                  min-height: 300px; white-space: pre-wrap; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Простой файловый менеджер</h1>
        <button id="selectBtn">Выбрать файл</button>
        <button id="clearBtn">Очистить содержимое</button>
       
        <div>
            <h3>Содержимое файла:</h3>
            <div id="content">Пожалуйста, выберите файл...</div>
        </div>
    </div>

    <script>
        const selectBtn = document.getElementById('selectBtn');
        const clearBtn = document.getElementById('clearBtn');
        const contentDiv = document.getElementById('content');

        selectBtn.addEventListener('click', async () => {
            const result = await window.fileAPI.selectFile();
           
            if (!result.canceled && result.filePaths.length > 0) {
               const filePath = result.filePaths[0];
                const fileResult = await window.fileAPI.readFile(filePath);
               
                if (fileResult.success) {
contentDiv.textContent = `Путь к файлу: ${filePath}\n\n${fileResult.content}`;
                } else {
contentDiv.textContent = `Не удалось прочитать файл: ${fileResult.error}`;
                }
            }
        });

        clearBtn.addEventListener('click', () => {
contentDiv.textContent = 'Пожалуйста, выберите файл...';
        });
    </script>
</body>
</html>

Другие важные модули (доступны в основном процессе)

имя модуля Функция
app Управление жизненным циклом приложения (запуск/выход)
Menu / MenuItem Создание строки меню приложения
Tray Значок системного трея
Notification Системное уведомление
dialog Диалоговое окно открытия/сохранения файла
shell Вызов системной программы по умолчанию для открытия файлов или URL
nativeImage Работа с изображениями (значками)
clipboard Работа с содержимым буфера обмена
powerMonitor Прослушивание системных событий питания
screen Получение информации об экране (поддержка нескольких мониторов)

Архитектура безопасности и изоляция песочницы

Поскольку процессы рендеринга могут запускать веб-страницы, чтобы предотвратить атаки вредоносного кода на систему, Electron реализует следующие механизмы безопасности:

Политика Описание
Отключение NodeIntegration Defaultfalse, предотвращая прямой вызов системных API веб-страницами.
Включите ContextIsolation Заставьте скрипты страницы и Preload APIs выполняться в изолированном контексте.
Preload + contextBridge Чётко контролируйте безопасные API, предоставляемые фронтенду.
Content Security Policy (CSP) Ограничьте источники скриптов для предотвращения XSS.
режим sandbox Песочницу можно включить, чтобы полностью изолировать процесс рендеринга.
Проверяйте удалённые URL Недоверенное удалённое содержимое должно проходить проверку по белому списку.

Поток выполнения (от запуска до рендеринга)

1、启动应用 (electron .)
     ↓
2、执行 main.js(主进程启动)
     ↓
3、app.whenReady() 触发,创建 BrowserWindow
     ↓
4、BrowserWindow 启动新的渲染进程(Chromium 实例)
     ↓
5、preload.js 先执行(在隔离上下文中)
     ↓
6、index.html 加载并显示(运行前端框架)
     ↓
7、渲染进程通过 IPC 调用主进程逻辑
     ↓
8、主进程处理请求、返回结果
     ↓
9、用户关闭窗口 → 主进程监听 → app.quit()

Модель главного-подчинённого многоконного режима (параллельная многопроцессность)

  • У приложения Electron всегда есть толькоГлавный процесс;
  • Но может иметьНесколько процессов рендеринга (каждое окно независимо);
  • Связь между ними должна проходить через ретрансляцию главного процесса или использоватьipcMain.handleВыполните асинхронное мостовое соединение.

Схема:

主进程 (Main)
 ├── 窗口1 → 渲染进程A (index.html)
 ├── 窗口2 → 渲染进程B (settings.html)
 └── 窗口3 → 渲染进程C (dashboard.html)

Внутренние основные компоненты Electron (базовые зависимости)

Уровень Технология
Слой рендеринга UI Chromium(Blink + V8)
Слой системного интерфейса Node.js(libuv + привязки C++)
Слой управления процессами Ядро Electron (C++ + JavaScript)
Слой прикладной логики Ваш код JS/TS (Main + Renderer)

Ваш JS-код выполняется в среде Node, инкапсулированной Electron, а интерфейс рендеринга работает в Chromium WebView.

Другие расширения